From 47e6e7c84f008a53061e661f31ae96629bc694ef Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sun, 26 Oct 2014 12:33:50 +0400 Subject: Debian 3.9.10 --- src/pmdas/GNUmakefile | 61 + src/pmdas/aix/GNUmakefile | 69 + src/pmdas/aix/aix.c | 127 + src/pmdas/aix/common.h | 104 + src/pmdas/aix/cpu.c | 186 + src/pmdas/aix/cpu_total.c | 133 + src/pmdas/aix/data.c | 435 ++ src/pmdas/aix/disk.c | 189 + src/pmdas/aix/disk_total.c | 127 + src/pmdas/aix/help | 61 + src/pmdas/aix/netif.c | 190 + src/pmdas/aix/pmns.disk | 22 + src/pmdas/aix/pmns.hinv | 6 + src/pmdas/aix/pmns.kernel | 77 + src/pmdas/aix/pmns.mem | 2 + src/pmdas/aix/pmns.network | 26 + src/pmdas/aix/root | 20 + src/pmdas/apache/Apache.pmchart | 9 + src/pmdas/apache/GNUmakefile | 61 + src/pmdas/apache/Install | 54 + src/pmdas/apache/README | 84 + src/pmdas/apache/Remove | 25 + src/pmdas/apache/apache.c | 538 +++ src/pmdas/apache/help | 91 + src/pmdas/apache/pmlogconf.processes | 14 + src/pmdas/apache/pmlogconf.summary | 10 + src/pmdas/apache/pmlogconf.uptime | 6 + src/pmdas/apache/pmns | 43 + src/pmdas/apache/root | 10 + src/pmdas/bash/GNUmakefile | 62 + src/pmdas/bash/Install | 36 + src/pmdas/bash/README | 50 + src/pmdas/bash/Remove | 23 + src/pmdas/bash/bash.c | 472 ++ src/pmdas/bash/bashproc.sh | 64 + src/pmdas/bash/event.c | 434 ++ src/pmdas/bash/event.h | 65 + src/pmdas/bash/help | 48 + src/pmdas/bash/pcp.sh | 30 + src/pmdas/bash/pmns | 36 + src/pmdas/bash/root | 10 + src/pmdas/bash/test-child.sh | 39 + src/pmdas/bash/test-trace.sh | 41 + src/pmdas/bash/util.c | 84 + src/pmdas/bonding/GNUmakefile | 56 + src/pmdas/bonding/Install | 41 + src/pmdas/bonding/Remove | 29 + src/pmdas/bonding/pmdabonding.pl | 154 + src/pmdas/cisco/Cisco.pmchart | 11 + src/pmdas/cisco/GNUmakefile | 70 + src/pmdas/cisco/Install | 156 + src/pmdas/cisco/README | 76 + src/pmdas/cisco/Remove | 38 + src/pmdas/cisco/Samples | 291 ++ src/pmdas/cisco/Tested | 76 + src/pmdas/cisco/cisco.c | 265 ++ src/pmdas/cisco/cisco.h | 86 + src/pmdas/cisco/cisco.in_util.pmie | 64 + src/pmdas/cisco/cisco.out_util.pmie | 64 + src/pmdas/cisco/help | 76 + src/pmdas/cisco/interface.c | 46 + src/pmdas/cisco/parse.sh | 3 + src/pmdas/cisco/pmda.c | 401 ++ src/pmdas/cisco/pmns | 28 + src/pmdas/cisco/probe.c | 367 ++ src/pmdas/cisco/root | 10 + src/pmdas/cisco/telnet.c | 755 +++ src/pmdas/darwin/GNUmakefile | 69 + src/pmdas/darwin/disk.c | 261 + src/pmdas/darwin/disk.h | 55 + src/pmdas/darwin/help | 199 + src/pmdas/darwin/kernel.c | 195 + src/pmdas/darwin/network.c | 185 + src/pmdas/darwin/network.h | 57 + src/pmdas/darwin/pmda.c | 1268 +++++ src/pmdas/darwin/pmns | 258 + src/pmdas/darwin/root | 15 + src/pmdas/dbping/GNUmakefile | 56 + src/pmdas/dbping/Install | 36 + src/pmdas/dbping/Remove | 29 + src/pmdas/dbping/dbprobe.pl | 93 + src/pmdas/dbping/pmdadbping.pl | 192 + src/pmdas/dmcache/GNUmakefile | 37 + src/pmdas/dmcache/Install | 35 + src/pmdas/dmcache/Remove | 25 + src/pmdas/dmcache/pmdadmcache.python | 264 ++ src/pmdas/dtsrun/GNUmakefile | 52 + src/pmdas/dtsrun/Install | 42 + src/pmdas/dtsrun/Remove | 25 + src/pmdas/dtsrun/pmdadtsrun.pl | 341 ++ src/pmdas/elasticsearch/GNUmakefile | 54 + src/pmdas/elasticsearch/Install | 38 + src/pmdas/elasticsearch/Remove | 25 + src/pmdas/elasticsearch/pmdaelasticsearch.pl | 882 ++++ src/pmdas/etw/GNUmakefile | 78 + src/pmdas/etw/event.c | 251 + src/pmdas/etw/event.h | 94 + src/pmdas/etw/help | 68 + src/pmdas/etw/pcp.xml | Bin 0 -> 107596 bytes src/pmdas/etw/pmda.c | 513 ++ src/pmdas/etw/pmns | 132 + src/pmdas/etw/root | 10 + src/pmdas/etw/tdhconsume.c | 923 ++++ src/pmdas/etw/tdhlist.c | 267 ++ src/pmdas/etw/util.c | 190 + src/pmdas/etw/util.h | 31 + src/pmdas/freebsd/GNUmakefile | 69 + src/pmdas/freebsd/disk.c | 208 + src/pmdas/freebsd/freebsd.c | 991 ++++ src/pmdas/freebsd/freebsd.h | 44 + src/pmdas/freebsd/help | 95 + src/pmdas/freebsd/netif.c | 225 + src/pmdas/freebsd/root_freebsd | 172 + src/pmdas/gfs2/GNUmakefile | 60 + src/pmdas/gfs2/Install | 27 + src/pmdas/gfs2/README | 108 + src/pmdas/gfs2/Remove | 24 + src/pmdas/gfs2/control.c | 123 + src/pmdas/gfs2/control.h | 49 + src/pmdas/gfs2/ftrace.c | 586 +++ src/pmdas/gfs2/ftrace.h | 142 + src/pmdas/gfs2/glocks.c | 93 + src/pmdas/gfs2/glocks.h | 36 + src/pmdas/gfs2/glstats.c | 117 + src/pmdas/gfs2/glstats.h | 57 + src/pmdas/gfs2/help | 566 +++ src/pmdas/gfs2/latency.c | 373 ++ src/pmdas/gfs2/latency.h | 70 + src/pmdas/gfs2/pmda.c | 1063 +++++ src/pmdas/gfs2/pmdagfs2.h | 57 + src/pmdas/gfs2/pmns | 270 ++ src/pmdas/gfs2/root | 16 + src/pmdas/gfs2/sbstats.c | 264 ++ src/pmdas/gfs2/sbstats.h | 56 + src/pmdas/gfs2/worst_glock.c | 391 ++ src/pmdas/gfs2/worst_glock.h | 91 + src/pmdas/gluster/GNUmakefile | 37 + src/pmdas/gluster/Install | 28 + src/pmdas/gluster/Remove | 25 + src/pmdas/gluster/pmdagluster.python | 337 ++ src/pmdas/gpsd/GNUmakefile | 48 + src/pmdas/gpsd/Install | 34 + src/pmdas/gpsd/Remove | 25 + src/pmdas/gpsd/pmdagpsd.pl | 310 ++ src/pmdas/hotproc/GNUakefile | 52 + src/pmdas/hotproc/GNUmakefile | 157 + src/pmdas/hotproc/Install | 150 + src/pmdas/hotproc/README | 141 + src/pmdas/hotproc/Remove | 38 + src/pmdas/hotproc/fixpmns.awk | 35 + src/pmdas/hotproc/general.conf | 27 + src/pmdas/hotproc/general.pmie | 29 + src/pmdas/hotproc/help.fmt | 36 + src/pmdas/hotproc/help.hotproc | 141 + src/pmdas/hotproc/pmns.hotproc | 34 + src/pmdas/hotproc/root | 10 + src/pmdas/hotproc/sample.conf | 15 + src/pmdas/hotproc/src/GNUmakefile | 34 + src/pmdas/hotproc/src/config.c | 569 +++ src/pmdas/hotproc/src/config.h | 79 + src/pmdas/hotproc/src/ctltab.c | 66 + src/pmdas/hotproc/src/error.c | 40 + src/pmdas/hotproc/src/gram.y | 163 + src/pmdas/hotproc/src/gram_node.c | 199 + src/pmdas/hotproc/src/gram_node.h | 69 + src/pmdas/hotproc/src/hotproc.c | 1555 ++++++ src/pmdas/hotproc/src/hotproc.h | 52 + src/pmdas/hotproc/src/lex.l | 115 + src/pmdas/hotproc/src/pcpu.c | 100 + src/pmdas/hotproc/src/pcpu.h | 31 + src/pmdas/hotproc/src/pglobal.c | 93 + src/pmdas/hotproc/src/pglobal.h | 41 + src/pmdas/hotproc/src/ppred_values.c | 163 + src/pmdas/hotproc/src/ppred_values.h | 39 + src/pmdas/infiniband/GNUmakefile | 58 + src/pmdas/infiniband/Install | 46 + src/pmdas/infiniband/Remove | 24 + src/pmdas/infiniband/help | 190 + src/pmdas/infiniband/ib.c | 1050 ++++ src/pmdas/infiniband/ibpmda.h | 125 + src/pmdas/infiniband/pmda.c | 418 ++ src/pmdas/infiniband/pmns | 100 + src/pmdas/infiniband/root | 9 + src/pmdas/jbd2/GNUmakefile | 73 + src/pmdas/jbd2/Install | 31 + src/pmdas/jbd2/Remove | 23 + src/pmdas/jbd2/convert.h | 30 + src/pmdas/jbd2/help | 115 + src/pmdas/jbd2/pmda.c | 322 ++ src/pmdas/jbd2/proc_jbd2.c | 148 + src/pmdas/jbd2/proc_jbd2.h | 38 + src/pmdas/jbd2/root | 6 + src/pmdas/jbd2/root_jbd2 | 63 + src/pmdas/kvm/GNUmakefile | 56 + src/pmdas/kvm/Install | 40 + src/pmdas/kvm/Remove | 25 + src/pmdas/kvm/pmdakvm.pl | 122 + src/pmdas/linux/GNUmakefile | 128 + src/pmdas/linux/clusters.h | 82 + src/pmdas/linux/convert.h | 49 + src/pmdas/linux/devmapper.c | 86 + src/pmdas/linux/devmapper.h | 29 + src/pmdas/linux/filesys.c | 122 + src/pmdas/linux/filesys.h | 32 + src/pmdas/linux/getinfo.c | 93 + src/pmdas/linux/getinfo.h | 16 + src/pmdas/linux/help | 1122 +++++ src/pmdas/linux/indom.h | 69 + src/pmdas/linux/interrupts.c | 394 ++ src/pmdas/linux/interrupts.h | 19 + src/pmdas/linux/linux_table.c | 116 + src/pmdas/linux/linux_table.h | 66 + src/pmdas/linux/msg_limits.c | 49 + src/pmdas/linux/msg_limits.h | 35 + src/pmdas/linux/numa_meminfo.c | 137 + src/pmdas/linux/numa_meminfo.h | 32 + src/pmdas/linux/pmda.c | 5807 +++++++++++++++++++++++ src/pmdas/linux/proc_cpuinfo.c | 246 + src/pmdas/linux/proc_cpuinfo.h | 49 + src/pmdas/linux/proc_loadavg.c | 45 + src/pmdas/linux/proc_loadavg.h | 29 + src/pmdas/linux/proc_meminfo.c | 188 + src/pmdas/linux/proc_meminfo.h | 79 + src/pmdas/linux/proc_net_dev.c | 444 ++ src/pmdas/linux/proc_net_dev.h | 100 + src/pmdas/linux/proc_net_netstat.c | 354 ++ src/pmdas/linux/proc_net_netstat.h | 150 + src/pmdas/linux/proc_net_rpc.c | 188 + src/pmdas/linux/proc_net_rpc.h | 99 + src/pmdas/linux/proc_net_snmp.c | 367 ++ src/pmdas/linux/proc_net_snmp.h | 136 + src/pmdas/linux/proc_net_snmp_migrate.conf | 8 + src/pmdas/linux/proc_net_sockstat.c | 65 + src/pmdas/linux/proc_net_sockstat.h | 29 + src/pmdas/linux/proc_net_tcp.c | 71 + src/pmdas/linux/proc_net_tcp.h | 44 + src/pmdas/linux/proc_partitions.c | 808 ++++ src/pmdas/linux/proc_partitions.h | 44 + src/pmdas/linux/proc_scsi.c | 159 + src/pmdas/linux/proc_scsi.h | 38 + src/pmdas/linux/proc_slabinfo.c | 237 + src/pmdas/linux/proc_slabinfo.h | 53 + src/pmdas/linux/proc_stat.c | 304 ++ src/pmdas/linux/proc_stat.h | 65 + src/pmdas/linux/proc_sys_fs.c | 80 + src/pmdas/linux/proc_sys_fs.h | 32 + src/pmdas/linux/proc_uptime.c | 47 + src/pmdas/linux/proc_uptime.h | 29 + src/pmdas/linux/proc_vmstat.c | 299 ++ src/pmdas/linux/proc_vmstat.h | 131 + src/pmdas/linux/root_linux | 1005 ++++ src/pmdas/linux/sem_limits.c | 51 + src/pmdas/linux/sem_limits.h | 49 + src/pmdas/linux/shm_limits.c | 43 + src/pmdas/linux/shm_limits.h | 32 + src/pmdas/linux/swapdev.c | 72 + src/pmdas/linux/swapdev.h | 28 + src/pmdas/linux/sysfs_kernel.c | 41 + src/pmdas/linux/sysfs_kernel.h | 34 + src/pmdas/linux_proc/GNUmakefile | 89 + src/pmdas/linux_proc/Install | 29 + src/pmdas/linux_proc/Remove | 23 + src/pmdas/linux_proc/cgroups.c | 1146 +++++ src/pmdas/linux_proc/cgroups.h | 74 + src/pmdas/linux_proc/clusters.h | 48 + src/pmdas/linux_proc/contexts.c | 238 + src/pmdas/linux_proc/contexts.h | 57 + src/pmdas/linux_proc/getinfo.c | 55 + src/pmdas/linux_proc/getinfo.h | 16 + src/pmdas/linux_proc/help | 220 + src/pmdas/linux_proc/indom.h | 52 + src/pmdas/linux_proc/ksym.c | 564 +++ src/pmdas/linux_proc/ksym.h | 41 + src/pmdas/linux_proc/linux_proc_migrate.conf | 55 + src/pmdas/linux_proc/pmda.c | 1896 ++++++++ src/pmdas/linux_proc/proc_pid.c | 957 ++++ src/pmdas/linux_proc/proc_pid.h | 289 ++ src/pmdas/linux_proc/proc_runq.c | 123 + src/pmdas/linux_proc/proc_runq.h | 35 + src/pmdas/linux_proc/root | 6 + src/pmdas/linux_proc/root_proc | 181 + src/pmdas/linux_xfs/GNUmakefile | 76 + src/pmdas/linux_xfs/Install | 29 + src/pmdas/linux_xfs/Remove | 23 + src/pmdas/linux_xfs/clusters.h | 31 + src/pmdas/linux_xfs/filesys.c | 183 + src/pmdas/linux_xfs/filesys.h | 108 + src/pmdas/linux_xfs/help | 469 ++ src/pmdas/linux_xfs/indom.h | 31 + src/pmdas/linux_xfs/linux_xfs_migrate.conf | 16 + src/pmdas/linux_xfs/pmda.c | 979 ++++ src/pmdas/linux_xfs/proc_fs_xfs.c | 278 ++ src/pmdas/linux_xfs/proc_fs_xfs.h | 189 + src/pmdas/linux_xfs/root | 6 + src/pmdas/linux_xfs/root_xfs | 295 ++ src/pmdas/lmsensors/GNUmakefile | 61 + src/pmdas/lmsensors/Install | 35 + src/pmdas/lmsensors/README | 69 + src/pmdas/lmsensors/Remove | 38 + src/pmdas/lmsensors/help | 1 + src/pmdas/lmsensors/lmsensors.c | 953 ++++ src/pmdas/lmsensors/lmsensors.h | 113 + src/pmdas/lmsensors/pmns | 106 + src/pmdas/lmsensors/root | 12 + src/pmdas/logger/GNUmakefile | 56 + src/pmdas/logger/Install | 164 + src/pmdas/logger/README | 51 + src/pmdas/logger/Remove | 24 + src/pmdas/logger/event.c | 493 ++ src/pmdas/logger/event.h | 56 + src/pmdas/logger/help | 37 + src/pmdas/logger/logger.c | 587 +++ src/pmdas/logger/pmns | 23 + src/pmdas/logger/root | 10 + src/pmdas/logger/util.c | 181 + src/pmdas/logger/util.h | 25 + src/pmdas/lustrecomm/GNUmakefile | 63 + src/pmdas/lustrecomm/Install | 27 + src/pmdas/lustrecomm/README | 60 + src/pmdas/lustrecomm/Remove | 38 + src/pmdas/lustrecomm/TODO | 3 + src/pmdas/lustrecomm/file_indexed.c | 96 + src/pmdas/lustrecomm/file_single.c | 104 + src/pmdas/lustrecomm/help | 106 + src/pmdas/lustrecomm/libreadfiles.h | 59 + src/pmdas/lustrecomm/lustrecomm.c | 304 ++ src/pmdas/lustrecomm/pmns | 47 + src/pmdas/lustrecomm/pmns.v4 | 87 + src/pmdas/lustrecomm/refresh_file.c | 85 + src/pmdas/lustrecomm/root | 10 + src/pmdas/lustrecomm/timespec_routines.c | 48 + src/pmdas/mailq/GNUmakefile | 49 + src/pmdas/mailq/Install | 117 + src/pmdas/mailq/README | 48 + src/pmdas/mailq/Remove | 38 + src/pmdas/mailq/help | 52 + src/pmdas/mailq/mailq.c | 401 ++ src/pmdas/mailq/pmlogconf.summary | 5 + src/pmdas/mailq/pmns | 24 + src/pmdas/mailq/root | 10 + src/pmdas/memcache/GNUmakefile | 54 + src/pmdas/memcache/Install | 28 + src/pmdas/memcache/Remove | 28 + src/pmdas/memcache/client.pl | 24 + src/pmdas/memcache/pmdamemcache.pl | 307 ++ src/pmdas/mmv/GNUmakefile | 62 + src/pmdas/mmv/Makefile.demos | 33 + src/pmdas/mmv/README.demos | 26 + src/pmdas/mmv/acme.c | 125 + src/pmdas/mmv/mmvdump.c | 346 ++ src/pmdas/mmv/src/GNUmakefile | 59 + src/pmdas/mmv/src/Install | 36 + src/pmdas/mmv/src/Remove | 23 + src/pmdas/mmv/src/mmv.c | 915 ++++ src/pmdas/mmv/src/root_mmv | 13 + src/pmdas/mounts/GNUmakefile | 64 + src/pmdas/mounts/Install | 30 + src/pmdas/mounts/README | 72 + src/pmdas/mounts/Remove | 41 + src/pmdas/mounts/help | 47 + src/pmdas/mounts/mounts.c | 384 ++ src/pmdas/mounts/mounts.conf | 10 + src/pmdas/mounts/pmns | 29 + src/pmdas/mounts/root | 15 + src/pmdas/mssql/GNUmakefile | 53 + src/pmdas/mssql/Install | 34 + src/pmdas/mssql/Remove | 29 + src/pmdas/mssql/pmdamssql.pl | 315 ++ src/pmdas/mysql/GNUmakefile | 52 + src/pmdas/mysql/Install | 34 + src/pmdas/mysql/README | 79 + src/pmdas/mysql/Remove | 29 + src/pmdas/mysql/migrate.conf | 23 + src/pmdas/mysql/pmdamysql.pl | 1911 ++++++++ src/pmdas/mysql/pmlogconf.summary | 4 + src/pmdas/named/GNUmakefile | 48 + src/pmdas/named/Install | 37 + src/pmdas/named/Remove | 25 + src/pmdas/named/pmdanamed.pl | 190 + src/pmdas/netbsd/GNUmakefile | 68 + src/pmdas/netbsd/disk.c | 216 + src/pmdas/netbsd/help | 95 + src/pmdas/netbsd/netbsd.c | 981 ++++ src/pmdas/netbsd/netbsd.h | 44 + src/pmdas/netbsd/netif.c | 233 + src/pmdas/netbsd/root_netbsd | 172 + src/pmdas/netfilter/GNUmakefile | 55 + src/pmdas/netfilter/Install | 33 + src/pmdas/netfilter/Remove | 25 + src/pmdas/netfilter/pmdanetfilter.pl | 100 + src/pmdas/netfilter/pmlogconf.config | 5 + src/pmdas/netfilter/pmlogconf.summary | 4 + src/pmdas/news/GNUmakefile | 53 + src/pmdas/news/Install | 28 + src/pmdas/news/README | 58 + src/pmdas/news/Remove | 29 + src/pmdas/news/active | 12 + src/pmdas/news/pmdanews.pl | 196 + src/pmdas/nfsclient/GNUmakefile | 49 + src/pmdas/nfsclient/Install | 29 + src/pmdas/nfsclient/Remove | 24 + src/pmdas/nfsclient/pmdanfsclient.pl | 1181 +++++ src/pmdas/nginx/GNUmakefile | 55 + src/pmdas/nginx/Install | 33 + src/pmdas/nginx/Remove | 23 + src/pmdas/nginx/nginx.conf | 2 + src/pmdas/nginx/pmdanginx.pl | 146 + src/pmdas/nvidia/GNUmakefile | 50 + src/pmdas/nvidia/Install | 28 + src/pmdas/nvidia/README | 7 + src/pmdas/nvidia/Remove | 38 + src/pmdas/nvidia/help | 72 + src/pmdas/nvidia/localnvml.c | 270 ++ src/pmdas/nvidia/localnvml.h | 89 + src/pmdas/nvidia/nvidia.c | 391 ++ src/pmdas/nvidia/pmns | 30 + src/pmdas/nvidia/root | 10 + src/pmdas/oracle/GNUmakefile | 49 + src/pmdas/oracle/Install | 63 + src/pmdas/oracle/Remove | 25 + src/pmdas/oracle/pmdaoracle.pl | 2364 +++++++++ src/pmdas/papi/GNUmakefile | 59 + src/pmdas/papi/Install | 27 + src/pmdas/papi/README | 54 + src/pmdas/papi/Remove | 38 + src/pmdas/papi/help | 74 + src/pmdas/papi/papi.c | 1497 ++++++ src/pmdas/papi/pmns | 139 + src/pmdas/papi/root | 9 + src/pmdas/pdns/GNUmakefile | 48 + src/pmdas/pdns/Install | 28 + src/pmdas/pdns/Remove | 25 + src/pmdas/pdns/pmdapdns.pl | 395 ++ src/pmdas/pmcd/GNUmakefile | 55 + src/pmdas/pmcd/help | 533 +++ src/pmdas/pmcd/root_pmcd | 153 + src/pmdas/pmcd/src/GNUmakefile | 68 + src/pmdas/pmcd/src/objstyle | 88 + src/pmdas/pmcd/src/pmcd.c | 1869 ++++++++ src/pmdas/postfix/GNUmakefile | 48 + src/pmdas/postfix/Install | 34 + src/pmdas/postfix/Remove | 25 + src/pmdas/postfix/pmdapostfix.pl | 266 ++ src/pmdas/postgresql/GNUmakefile | 51 + src/pmdas/postgresql/Install | 40 + src/pmdas/postgresql/Remove | 25 + src/pmdas/postgresql/pmdapostgresql.pl | 1546 ++++++ src/pmdas/postgresql/pmlogconf.summary | 8 + src/pmdas/process/GNUmakefile | 65 + src/pmdas/process/Install | 28 + src/pmdas/process/README | 74 + src/pmdas/process/Remove | 41 + src/pmdas/process/help | 38 + src/pmdas/process/pmns | 26 + src/pmdas/process/process.c | 413 ++ src/pmdas/process/process.conf | 11 + src/pmdas/process/root | 16 + src/pmdas/roomtemp/GNUmakefile | 66 + src/pmdas/roomtemp/Install | 55 + src/pmdas/roomtemp/README | 74 + src/pmdas/roomtemp/Remove | 38 + src/pmdas/roomtemp/dsread.c | 117 + src/pmdas/roomtemp/dsread.h | 4 + src/pmdas/roomtemp/help | 41 + src/pmdas/roomtemp/mlan/GNUmakefile | 45 + src/pmdas/roomtemp/mlan/ds2480.h | 183 + src/pmdas/roomtemp/mlan/ds2480ut.c | 206 + src/pmdas/roomtemp/mlan/linuxlnk.c | 443 ++ src/pmdas/roomtemp/mlan/mlan.h | 94 + src/pmdas/roomtemp/mlan/mlanllu.c | 499 ++ src/pmdas/roomtemp/mlan/mlannetu.c | 599 +++ src/pmdas/roomtemp/mlan/mlansesu.c | 102 + src/pmdas/roomtemp/mlan/mlantrnu.c | 579 +++ src/pmdas/roomtemp/pmns | 24 + src/pmdas/roomtemp/roomtemp.c | 211 + src/pmdas/roomtemp/root | 10 + src/pmdas/rpm/GNUmakefile | 67 + src/pmdas/rpm/Install | 31 + src/pmdas/rpm/Remove | 25 + src/pmdas/rpm/help | 57 + src/pmdas/rpm/migrate.conf | 8 + src/pmdas/rpm/pmns | 38 + src/pmdas/rpm/root | 10 + src/pmdas/rpm/rpm.c | 704 +++ src/pmdas/rpm/rpm.h | 111 + src/pmdas/rpm/timer.c | 45 + src/pmdas/rpm/timer.h | 24 + src/pmdas/rsyslog/GNUmakefile | 48 + src/pmdas/rsyslog/Install | 44 + src/pmdas/rsyslog/Remove | 25 + src/pmdas/rsyslog/pmdarsyslog.pl | 249 + src/pmdas/samba/GNUmakefile | 52 + src/pmdas/samba/Install | 43 + src/pmdas/samba/Remove | 25 + src/pmdas/samba/pmdasamba.pl | 198 + src/pmdas/sample/GNUmakefile | 40 + src/pmdas/sample/Install | 74 + src/pmdas/sample/README | 63 + src/pmdas/sample/Remove | 45 + src/pmdas/sample/Sample.pmchart | 10 + src/pmdas/sample/domain.h | 4 + src/pmdas/sample/get_next_pmid | 48 + src/pmdas/sample/help | 602 +++ src/pmdas/sample/pmns | 260 + src/pmdas/sample/root | 20 + src/pmdas/sample/src/GNUmakefile | 62 + src/pmdas/sample/src/GNUmakefile.install | 53 + src/pmdas/sample/src/events.c | 477 ++ src/pmdas/sample/src/events.h | 33 + src/pmdas/sample/src/percontext.c | 256 + src/pmdas/sample/src/percontext.h | 31 + src/pmdas/sample/src/pmda.c | 126 + src/pmdas/sample/src/sample.c | 2748 +++++++++++ src/pmdas/sendmail/GNUmakefile | 58 + src/pmdas/sendmail/Install | 27 + src/pmdas/sendmail/README | 50 + src/pmdas/sendmail/Remove | 38 + src/pmdas/sendmail/Sendmail.pmchart | 10 + src/pmdas/sendmail/help | 73 + src/pmdas/sendmail/pmns | 39 + src/pmdas/sendmail/root | 10 + src/pmdas/sendmail/sendmail.c | 524 ++ src/pmdas/shping/GNUmakefile | 69 + src/pmdas/shping/Install | 222 + src/pmdas/shping/README | 91 + src/pmdas/shping/Remove | 38 + src/pmdas/shping/help | 137 + src/pmdas/shping/pmda.c | 257 + src/pmdas/shping/pmlogconf.summary | 6 + src/pmdas/shping/pmns | 41 + src/pmdas/shping/root | 9 + src/pmdas/shping/sample.conf | 46 + src/pmdas/shping/shping.CPUTime.pmchart | 11 + src/pmdas/shping/shping.RealTime.pmchart | 9 + src/pmdas/shping/shping.c | 679 +++ src/pmdas/shping/shping.h | 54 + src/pmdas/shping/shping.response.pmie | 67 + src/pmdas/shping/shping.status.pmie | 63 + src/pmdas/simple/GNUmakefile | 46 + src/pmdas/simple/GNUmakefile.install | 53 + src/pmdas/simple/Install | 53 + src/pmdas/simple/README | 67 + src/pmdas/simple/Remove | 25 + src/pmdas/simple/help | 82 + src/pmdas/simple/pmdasimple.perl | 158 + src/pmdas/simple/pmdasimple.python | 244 + src/pmdas/simple/pmns | 27 + src/pmdas/simple/root | 10 + src/pmdas/simple/simple.c | 517 ++ src/pmdas/simple/simple.conf | 1 + src/pmdas/snmp/GNUmakefile | 53 + src/pmdas/snmp/Install | 28 + src/pmdas/snmp/README | 52 + src/pmdas/snmp/Remove | 29 + src/pmdas/snmp/pmdasnmp.pl | 413 ++ src/pmdas/snmp/snmp.conf | 44 + src/pmdas/solaris/GNUmakefile | 82 + src/pmdas/solaris/arcstats.c | 55 + src/pmdas/solaris/clusters.h | 20 + src/pmdas/solaris/common.h | 137 + src/pmdas/solaris/data.c | 1462 ++++++ src/pmdas/solaris/disk.c | 420 ++ src/pmdas/solaris/help | 729 +++ src/pmdas/solaris/kvm.c | 59 + src/pmdas/solaris/netlink.c | 125 + src/pmdas/solaris/netmib2.c | 329 ++ src/pmdas/solaris/netmib2.h | 54 + src/pmdas/solaris/pmns.disk | 58 + src/pmdas/solaris/pmns.hinv | 34 + src/pmdas/solaris/pmns.kernel | 200 + src/pmdas/solaris/pmns.mem | 88 + src/pmdas/solaris/pmns.network | 302 ++ src/pmdas/solaris/pmns.zfs | 60 + src/pmdas/solaris/pmns.zpool | 31 + src/pmdas/solaris/pmns.zpool_perdisk | 16 + src/pmdas/solaris/root | 42 + src/pmdas/solaris/solaris.c | 216 + src/pmdas/solaris/sysinfo.c | 376 ++ src/pmdas/solaris/vnops.c | 221 + src/pmdas/solaris/zfs.c | 171 + src/pmdas/solaris/zpool.c | 154 + src/pmdas/solaris/zpool_perdisk.c | 289 ++ src/pmdas/summary/GNUmakefile | 62 + src/pmdas/summary/Install | 51 + src/pmdas/summary/README | 255 + src/pmdas/summary/Remove | 38 + src/pmdas/summary/help | 56 + src/pmdas/summary/mainloop.c | 225 + src/pmdas/summary/pmda.c | 152 + src/pmdas/summary/pmns | 24 + src/pmdas/summary/root | 10 + src/pmdas/summary/summary.c | 300 ++ src/pmdas/summary/summary.h | 34 + src/pmdas/summary/summary.pmie | 40 + src/pmdas/systemd/GNUmakefile | 59 + src/pmdas/systemd/Install | 29 + src/pmdas/systemd/README | 64 + src/pmdas/systemd/Remove | 24 + src/pmdas/systemd/help | 52 + src/pmdas/systemd/pmns | 35 + src/pmdas/systemd/root | 11 + src/pmdas/systemd/systemd.c | 791 +++ src/pmdas/systemtap/GNUmakefile | 56 + src/pmdas/systemtap/Install | 32 + src/pmdas/systemtap/README | 59 + src/pmdas/systemtap/Remove | 29 + src/pmdas/systemtap/pmdasystemtap.pl | 168 + src/pmdas/systemtap/probes.stp | 6 + src/pmdas/trace/GNUmakefile | 109 + src/pmdas/trace/GNUmakefile.stub | 69 + src/pmdas/trace/Install | 275 ++ src/pmdas/trace/Makefile.proto | 69 + src/pmdas/trace/README | 62 + src/pmdas/trace/README.demos | 71 + src/pmdas/trace/Remove | 38 + src/pmdas/trace/app1.c | 97 + src/pmdas/trace/app2.c | 163 + src/pmdas/trace/app3.c | 166 + src/pmdas/trace/fapp1.f | 102 + src/pmdas/trace/help | 156 + src/pmdas/trace/japp1.java | 46 + src/pmdas/trace/pmns | 62 + src/pmdas/trace/root | 10 + src/pmdas/trace/src/GNUmakefile | 57 + src/pmdas/trace/src/client.c | 155 + src/pmdas/trace/src/client.h | 38 + src/pmdas/trace/src/comms.c | 289 ++ src/pmdas/trace/src/comms.h | 26 + src/pmdas/trace/src/data.c | 122 + src/pmdas/trace/src/data.h | 68 + src/pmdas/trace/src/pmda.c | 228 + src/pmdas/trace/src/trace.c | 1151 +++++ src/pmdas/trace/stub.c | 136 + src/pmdas/trivial/GNUmakefile | 43 + src/pmdas/trivial/GNUmakefile.install | 51 + src/pmdas/trivial/Install | 27 + src/pmdas/trivial/README | 63 + src/pmdas/trivial/Remove | 38 + src/pmdas/trivial/help | 35 + src/pmdas/trivial/pmns | 23 + src/pmdas/trivial/root | 10 + src/pmdas/trivial/trivial.c | 138 + src/pmdas/txmon/GNUmakefile | 68 + src/pmdas/txmon/GNUmakefile.install | 57 + src/pmdas/txmon/Install | 56 + src/pmdas/txmon/README | 77 + src/pmdas/txmon/Remove | 38 + src/pmdas/txmon/genload | 120 + src/pmdas/txmon/help | 62 + src/pmdas/txmon/pmns | 32 + src/pmdas/txmon/root | 10 + src/pmdas/txmon/txmon.c | 383 ++ src/pmdas/txmon/txmon.h | 53 + src/pmdas/txmon/txrecord.c | 108 + src/pmdas/vmware/GNUmakefile | 54 + src/pmdas/vmware/Install | 34 + src/pmdas/vmware/Remove | 29 + src/pmdas/vmware/pmdavmware.pl | 456 ++ src/pmdas/weblog/GNUmakefile | 75 + src/pmdas/weblog/Install | 694 +++ src/pmdas/weblog/README | 205 + src/pmdas/weblog/Remove | 43 + src/pmdas/weblog/Web.Alarms.pmchart | 22 + src/pmdas/weblog/Web.Allservers.pmchart | 90 + src/pmdas/weblog/Web.Perserver.Bytes.pmchart | 90 + src/pmdas/weblog/Web.Perserver.Requests.pmchart | 91 + src/pmdas/weblog/Web.Requests.pmchart | 27 + src/pmdas/weblog/Web.Volume.pmchart | 25 + src/pmdas/weblog/check_match.c | 414 ++ src/pmdas/weblog/help | 654 +++ src/pmdas/weblog/pmda.c | 1205 +++++ src/pmdas/weblog/pmns | 306 ++ src/pmdas/weblog/root | 10 + src/pmdas/weblog/server.sh | 1228 +++++ src/pmdas/weblog/sproc.c | 39 + src/pmdas/weblog/weblog.c | 3132 ++++++++++++ src/pmdas/weblog/weblog.h | 140 + src/pmdas/weblog/weblogconv.sh | 62 + src/pmdas/windows/GNUmakefile | 81 + src/pmdas/windows/README | 171 + src/pmdas/windows/error.c | 130 + src/pmdas/windows/fetch.c | 209 + src/pmdas/windows/help | 65 + src/pmdas/windows/helptext.c | 84 + src/pmdas/windows/hypnotoad.h | 104 + src/pmdas/windows/instance.c | 376 ++ src/pmdas/windows/open.c | 769 +++ src/pmdas/windows/pdhlist.c | 85 + src/pmdas/windows/pdhmatch.sh | 192 + src/pmdas/windows/pmda.c | 1601 +++++++ src/pmdas/windows/pmns.disk | 52 + src/pmdas/windows/pmns.filesys | 6 + src/pmdas/windows/pmns.hinv | 7 + src/pmdas/windows/pmns.kernel | 48 + src/pmdas/windows/pmns.mem | 61 + src/pmdas/windows/pmns.network | 42 + src/pmdas/windows/pmns.pmda | 4 + src/pmdas/windows/pmns.process | 59 + src/pmdas/windows/pmns.sqlserver | 147 + src/pmdas/windows/root | 29 + src/pmdas/zimbra/GNUmakefile | 51 + src/pmdas/zimbra/Install | 33 + src/pmdas/zimbra/Remove | 25 + src/pmdas/zimbra/pmdazimbra.pl | 905 ++++ src/pmdas/zimbra/pmlogconf.all | 4 + src/pmdas/zimbra/zimbraprobe.sh | 26 + src/pmdas/zswap/GNUmakefile | 37 + src/pmdas/zswap/Install | 28 + src/pmdas/zswap/Remove | 25 + src/pmdas/zswap/pmdazswap.python | 189 + 710 files changed, 122270 insertions(+) create mode 100644 src/pmdas/GNUmakefile create mode 100644 src/pmdas/aix/GNUmakefile create mode 100644 src/pmdas/aix/aix.c create mode 100644 src/pmdas/aix/common.h create mode 100644 src/pmdas/aix/cpu.c create mode 100644 src/pmdas/aix/cpu_total.c create mode 100644 src/pmdas/aix/data.c create mode 100644 src/pmdas/aix/disk.c create mode 100644 src/pmdas/aix/disk_total.c create mode 100644 src/pmdas/aix/help create mode 100644 src/pmdas/aix/netif.c create mode 100644 src/pmdas/aix/pmns.disk create mode 100644 src/pmdas/aix/pmns.hinv create mode 100644 src/pmdas/aix/pmns.kernel create mode 100644 src/pmdas/aix/pmns.mem create mode 100644 src/pmdas/aix/pmns.network create mode 100644 src/pmdas/aix/root create mode 100644 src/pmdas/apache/Apache.pmchart create mode 100644 src/pmdas/apache/GNUmakefile create mode 100755 src/pmdas/apache/Install create mode 100644 src/pmdas/apache/README create mode 100644 src/pmdas/apache/Remove create mode 100644 src/pmdas/apache/apache.c create mode 100644 src/pmdas/apache/help create mode 100644 src/pmdas/apache/pmlogconf.processes create mode 100644 src/pmdas/apache/pmlogconf.summary create mode 100644 src/pmdas/apache/pmlogconf.uptime create mode 100644 src/pmdas/apache/pmns create mode 100644 src/pmdas/apache/root create mode 100644 src/pmdas/bash/GNUmakefile create mode 100644 src/pmdas/bash/Install create mode 100644 src/pmdas/bash/README create mode 100644 src/pmdas/bash/Remove create mode 100644 src/pmdas/bash/bash.c create mode 100755 src/pmdas/bash/bashproc.sh create mode 100644 src/pmdas/bash/event.c create mode 100644 src/pmdas/bash/event.h create mode 100644 src/pmdas/bash/help create mode 100755 src/pmdas/bash/pcp.sh create mode 100644 src/pmdas/bash/pmns create mode 100644 src/pmdas/bash/root create mode 100755 src/pmdas/bash/test-child.sh create mode 100755 src/pmdas/bash/test-trace.sh create mode 100644 src/pmdas/bash/util.c create mode 100644 src/pmdas/bonding/GNUmakefile create mode 100755 src/pmdas/bonding/Install create mode 100755 src/pmdas/bonding/Remove create mode 100644 src/pmdas/bonding/pmdabonding.pl create mode 100644 src/pmdas/cisco/Cisco.pmchart create mode 100644 src/pmdas/cisco/GNUmakefile create mode 100644 src/pmdas/cisco/Install create mode 100644 src/pmdas/cisco/README create mode 100644 src/pmdas/cisco/Remove create mode 100644 src/pmdas/cisco/Samples create mode 100644 src/pmdas/cisco/Tested create mode 100644 src/pmdas/cisco/cisco.c create mode 100644 src/pmdas/cisco/cisco.h create mode 100644 src/pmdas/cisco/cisco.in_util.pmie create mode 100644 src/pmdas/cisco/cisco.out_util.pmie create mode 100644 src/pmdas/cisco/help create mode 100644 src/pmdas/cisco/interface.c create mode 100755 src/pmdas/cisco/parse.sh create mode 100644 src/pmdas/cisco/pmda.c create mode 100644 src/pmdas/cisco/pmns create mode 100644 src/pmdas/cisco/probe.c create mode 100644 src/pmdas/cisco/root create mode 100644 src/pmdas/cisco/telnet.c create mode 100644 src/pmdas/darwin/GNUmakefile create mode 100644 src/pmdas/darwin/disk.c create mode 100644 src/pmdas/darwin/disk.h create mode 100644 src/pmdas/darwin/help create mode 100644 src/pmdas/darwin/kernel.c create mode 100644 src/pmdas/darwin/network.c create mode 100644 src/pmdas/darwin/network.h create mode 100644 src/pmdas/darwin/pmda.c create mode 100644 src/pmdas/darwin/pmns create mode 100644 src/pmdas/darwin/root create mode 100644 src/pmdas/dbping/GNUmakefile create mode 100755 src/pmdas/dbping/Install create mode 100755 src/pmdas/dbping/Remove create mode 100644 src/pmdas/dbping/dbprobe.pl create mode 100644 src/pmdas/dbping/pmdadbping.pl create mode 100644 src/pmdas/dmcache/GNUmakefile create mode 100644 src/pmdas/dmcache/Install create mode 100644 src/pmdas/dmcache/Remove create mode 100644 src/pmdas/dmcache/pmdadmcache.python create mode 100644 src/pmdas/dtsrun/GNUmakefile create mode 100755 src/pmdas/dtsrun/Install create mode 100755 src/pmdas/dtsrun/Remove create mode 100644 src/pmdas/dtsrun/pmdadtsrun.pl create mode 100644 src/pmdas/elasticsearch/GNUmakefile create mode 100755 src/pmdas/elasticsearch/Install create mode 100755 src/pmdas/elasticsearch/Remove create mode 100755 src/pmdas/elasticsearch/pmdaelasticsearch.pl create mode 100644 src/pmdas/etw/GNUmakefile create mode 100644 src/pmdas/etw/event.c create mode 100644 src/pmdas/etw/event.h create mode 100644 src/pmdas/etw/help create mode 100644 src/pmdas/etw/pcp.xml create mode 100644 src/pmdas/etw/pmda.c create mode 100644 src/pmdas/etw/pmns create mode 100644 src/pmdas/etw/root create mode 100644 src/pmdas/etw/tdhconsume.c create mode 100644 src/pmdas/etw/tdhlist.c create mode 100644 src/pmdas/etw/util.c create mode 100644 src/pmdas/etw/util.h create mode 100644 src/pmdas/freebsd/GNUmakefile create mode 100644 src/pmdas/freebsd/disk.c create mode 100644 src/pmdas/freebsd/freebsd.c create mode 100644 src/pmdas/freebsd/freebsd.h create mode 100644 src/pmdas/freebsd/help create mode 100644 src/pmdas/freebsd/netif.c create mode 100644 src/pmdas/freebsd/root_freebsd create mode 100644 src/pmdas/gfs2/GNUmakefile create mode 100644 src/pmdas/gfs2/Install create mode 100644 src/pmdas/gfs2/README create mode 100644 src/pmdas/gfs2/Remove create mode 100644 src/pmdas/gfs2/control.c create mode 100644 src/pmdas/gfs2/control.h create mode 100644 src/pmdas/gfs2/ftrace.c create mode 100644 src/pmdas/gfs2/ftrace.h create mode 100644 src/pmdas/gfs2/glocks.c create mode 100644 src/pmdas/gfs2/glocks.h create mode 100644 src/pmdas/gfs2/glstats.c create mode 100644 src/pmdas/gfs2/glstats.h create mode 100644 src/pmdas/gfs2/help create mode 100644 src/pmdas/gfs2/latency.c create mode 100644 src/pmdas/gfs2/latency.h create mode 100644 src/pmdas/gfs2/pmda.c create mode 100644 src/pmdas/gfs2/pmdagfs2.h create mode 100644 src/pmdas/gfs2/pmns create mode 100644 src/pmdas/gfs2/root create mode 100644 src/pmdas/gfs2/sbstats.c create mode 100644 src/pmdas/gfs2/sbstats.h create mode 100644 src/pmdas/gfs2/worst_glock.c create mode 100644 src/pmdas/gfs2/worst_glock.h create mode 100644 src/pmdas/gluster/GNUmakefile create mode 100644 src/pmdas/gluster/Install create mode 100644 src/pmdas/gluster/Remove create mode 100644 src/pmdas/gluster/pmdagluster.python create mode 100644 src/pmdas/gpsd/GNUmakefile create mode 100644 src/pmdas/gpsd/Install create mode 100644 src/pmdas/gpsd/Remove create mode 100644 src/pmdas/gpsd/pmdagpsd.pl create mode 100644 src/pmdas/hotproc/GNUakefile create mode 100644 src/pmdas/hotproc/GNUmakefile create mode 100644 src/pmdas/hotproc/Install create mode 100644 src/pmdas/hotproc/README create mode 100644 src/pmdas/hotproc/Remove create mode 100644 src/pmdas/hotproc/fixpmns.awk create mode 100644 src/pmdas/hotproc/general.conf create mode 100755 src/pmdas/hotproc/general.pmie create mode 100755 src/pmdas/hotproc/help.fmt create mode 100644 src/pmdas/hotproc/help.hotproc create mode 100644 src/pmdas/hotproc/pmns.hotproc create mode 100644 src/pmdas/hotproc/root create mode 100644 src/pmdas/hotproc/sample.conf create mode 100644 src/pmdas/hotproc/src/GNUmakefile create mode 100644 src/pmdas/hotproc/src/config.c create mode 100644 src/pmdas/hotproc/src/config.h create mode 100644 src/pmdas/hotproc/src/ctltab.c create mode 100644 src/pmdas/hotproc/src/error.c create mode 100644 src/pmdas/hotproc/src/gram.y create mode 100644 src/pmdas/hotproc/src/gram_node.c create mode 100644 src/pmdas/hotproc/src/gram_node.h create mode 100644 src/pmdas/hotproc/src/hotproc.c create mode 100644 src/pmdas/hotproc/src/hotproc.h create mode 100644 src/pmdas/hotproc/src/lex.l create mode 100644 src/pmdas/hotproc/src/pcpu.c create mode 100644 src/pmdas/hotproc/src/pcpu.h create mode 100644 src/pmdas/hotproc/src/pglobal.c create mode 100644 src/pmdas/hotproc/src/pglobal.h create mode 100644 src/pmdas/hotproc/src/ppred_values.c create mode 100644 src/pmdas/hotproc/src/ppred_values.h create mode 100644 src/pmdas/infiniband/GNUmakefile create mode 100755 src/pmdas/infiniband/Install create mode 100755 src/pmdas/infiniband/Remove create mode 100644 src/pmdas/infiniband/help create mode 100644 src/pmdas/infiniband/ib.c create mode 100644 src/pmdas/infiniband/ibpmda.h create mode 100644 src/pmdas/infiniband/pmda.c create mode 100644 src/pmdas/infiniband/pmns create mode 100644 src/pmdas/infiniband/root create mode 100644 src/pmdas/jbd2/GNUmakefile create mode 100755 src/pmdas/jbd2/Install create mode 100755 src/pmdas/jbd2/Remove create mode 100644 src/pmdas/jbd2/convert.h create mode 100644 src/pmdas/jbd2/help create mode 100644 src/pmdas/jbd2/pmda.c create mode 100644 src/pmdas/jbd2/proc_jbd2.c create mode 100644 src/pmdas/jbd2/proc_jbd2.h create mode 100644 src/pmdas/jbd2/root create mode 100644 src/pmdas/jbd2/root_jbd2 create mode 100644 src/pmdas/kvm/GNUmakefile create mode 100755 src/pmdas/kvm/Install create mode 100755 src/pmdas/kvm/Remove create mode 100644 src/pmdas/kvm/pmdakvm.pl create mode 100644 src/pmdas/linux/GNUmakefile create mode 100644 src/pmdas/linux/clusters.h create mode 100644 src/pmdas/linux/convert.h create mode 100644 src/pmdas/linux/devmapper.c create mode 100644 src/pmdas/linux/devmapper.h create mode 100644 src/pmdas/linux/filesys.c create mode 100644 src/pmdas/linux/filesys.h create mode 100644 src/pmdas/linux/getinfo.c create mode 100644 src/pmdas/linux/getinfo.h create mode 100644 src/pmdas/linux/help create mode 100644 src/pmdas/linux/indom.h create mode 100644 src/pmdas/linux/interrupts.c create mode 100644 src/pmdas/linux/interrupts.h create mode 100644 src/pmdas/linux/linux_table.c create mode 100644 src/pmdas/linux/linux_table.h create mode 100644 src/pmdas/linux/msg_limits.c create mode 100644 src/pmdas/linux/msg_limits.h create mode 100644 src/pmdas/linux/numa_meminfo.c create mode 100644 src/pmdas/linux/numa_meminfo.h create mode 100644 src/pmdas/linux/pmda.c create mode 100644 src/pmdas/linux/proc_cpuinfo.c create mode 100644 src/pmdas/linux/proc_cpuinfo.h create mode 100644 src/pmdas/linux/proc_loadavg.c create mode 100644 src/pmdas/linux/proc_loadavg.h create mode 100644 src/pmdas/linux/proc_meminfo.c create mode 100644 src/pmdas/linux/proc_meminfo.h create mode 100644 src/pmdas/linux/proc_net_dev.c create mode 100644 src/pmdas/linux/proc_net_dev.h create mode 100644 src/pmdas/linux/proc_net_netstat.c create mode 100644 src/pmdas/linux/proc_net_netstat.h create mode 100644 src/pmdas/linux/proc_net_rpc.c create mode 100644 src/pmdas/linux/proc_net_rpc.h create mode 100644 src/pmdas/linux/proc_net_snmp.c create mode 100644 src/pmdas/linux/proc_net_snmp.h create mode 100644 src/pmdas/linux/proc_net_snmp_migrate.conf create mode 100644 src/pmdas/linux/proc_net_sockstat.c create mode 100644 src/pmdas/linux/proc_net_sockstat.h create mode 100644 src/pmdas/linux/proc_net_tcp.c create mode 100644 src/pmdas/linux/proc_net_tcp.h create mode 100644 src/pmdas/linux/proc_partitions.c create mode 100644 src/pmdas/linux/proc_partitions.h create mode 100644 src/pmdas/linux/proc_scsi.c create mode 100644 src/pmdas/linux/proc_scsi.h create mode 100644 src/pmdas/linux/proc_slabinfo.c create mode 100644 src/pmdas/linux/proc_slabinfo.h create mode 100644 src/pmdas/linux/proc_stat.c create mode 100644 src/pmdas/linux/proc_stat.h create mode 100644 src/pmdas/linux/proc_sys_fs.c create mode 100644 src/pmdas/linux/proc_sys_fs.h create mode 100644 src/pmdas/linux/proc_uptime.c create mode 100644 src/pmdas/linux/proc_uptime.h create mode 100644 src/pmdas/linux/proc_vmstat.c create mode 100644 src/pmdas/linux/proc_vmstat.h create mode 100644 src/pmdas/linux/root_linux create mode 100644 src/pmdas/linux/sem_limits.c create mode 100644 src/pmdas/linux/sem_limits.h create mode 100644 src/pmdas/linux/shm_limits.c create mode 100644 src/pmdas/linux/shm_limits.h create mode 100644 src/pmdas/linux/swapdev.c create mode 100644 src/pmdas/linux/swapdev.h create mode 100644 src/pmdas/linux/sysfs_kernel.c create mode 100644 src/pmdas/linux/sysfs_kernel.h create mode 100644 src/pmdas/linux_proc/GNUmakefile create mode 100755 src/pmdas/linux_proc/Install create mode 100755 src/pmdas/linux_proc/Remove create mode 100644 src/pmdas/linux_proc/cgroups.c create mode 100644 src/pmdas/linux_proc/cgroups.h create mode 100644 src/pmdas/linux_proc/clusters.h create mode 100644 src/pmdas/linux_proc/contexts.c create mode 100644 src/pmdas/linux_proc/contexts.h create mode 100644 src/pmdas/linux_proc/getinfo.c create mode 100644 src/pmdas/linux_proc/getinfo.h create mode 100644 src/pmdas/linux_proc/help create mode 100644 src/pmdas/linux_proc/indom.h create mode 100644 src/pmdas/linux_proc/ksym.c create mode 100644 src/pmdas/linux_proc/ksym.h create mode 100644 src/pmdas/linux_proc/linux_proc_migrate.conf create mode 100644 src/pmdas/linux_proc/pmda.c create mode 100644 src/pmdas/linux_proc/proc_pid.c create mode 100644 src/pmdas/linux_proc/proc_pid.h create mode 100644 src/pmdas/linux_proc/proc_runq.c create mode 100644 src/pmdas/linux_proc/proc_runq.h create mode 100644 src/pmdas/linux_proc/root create mode 100644 src/pmdas/linux_proc/root_proc create mode 100644 src/pmdas/linux_xfs/GNUmakefile create mode 100755 src/pmdas/linux_xfs/Install create mode 100755 src/pmdas/linux_xfs/Remove create mode 100644 src/pmdas/linux_xfs/clusters.h create mode 100644 src/pmdas/linux_xfs/filesys.c create mode 100644 src/pmdas/linux_xfs/filesys.h create mode 100644 src/pmdas/linux_xfs/help create mode 100644 src/pmdas/linux_xfs/indom.h create mode 100644 src/pmdas/linux_xfs/linux_xfs_migrate.conf create mode 100644 src/pmdas/linux_xfs/pmda.c create mode 100644 src/pmdas/linux_xfs/proc_fs_xfs.c create mode 100644 src/pmdas/linux_xfs/proc_fs_xfs.h create mode 100644 src/pmdas/linux_xfs/root create mode 100644 src/pmdas/linux_xfs/root_xfs create mode 100644 src/pmdas/lmsensors/GNUmakefile create mode 100755 src/pmdas/lmsensors/Install create mode 100644 src/pmdas/lmsensors/README create mode 100755 src/pmdas/lmsensors/Remove create mode 100644 src/pmdas/lmsensors/help create mode 100644 src/pmdas/lmsensors/lmsensors.c create mode 100644 src/pmdas/lmsensors/lmsensors.h create mode 100644 src/pmdas/lmsensors/pmns create mode 100644 src/pmdas/lmsensors/root create mode 100644 src/pmdas/logger/GNUmakefile create mode 100644 src/pmdas/logger/Install create mode 100644 src/pmdas/logger/README create mode 100644 src/pmdas/logger/Remove create mode 100644 src/pmdas/logger/event.c create mode 100644 src/pmdas/logger/event.h create mode 100644 src/pmdas/logger/help create mode 100644 src/pmdas/logger/logger.c create mode 100644 src/pmdas/logger/pmns create mode 100644 src/pmdas/logger/root create mode 100644 src/pmdas/logger/util.c create mode 100644 src/pmdas/logger/util.h create mode 100644 src/pmdas/lustrecomm/GNUmakefile create mode 100755 src/pmdas/lustrecomm/Install create mode 100644 src/pmdas/lustrecomm/README create mode 100755 src/pmdas/lustrecomm/Remove create mode 100644 src/pmdas/lustrecomm/TODO create mode 100644 src/pmdas/lustrecomm/file_indexed.c create mode 100644 src/pmdas/lustrecomm/file_single.c create mode 100644 src/pmdas/lustrecomm/help create mode 100644 src/pmdas/lustrecomm/libreadfiles.h create mode 100644 src/pmdas/lustrecomm/lustrecomm.c create mode 100644 src/pmdas/lustrecomm/pmns create mode 100644 src/pmdas/lustrecomm/pmns.v4 create mode 100644 src/pmdas/lustrecomm/refresh_file.c create mode 100644 src/pmdas/lustrecomm/root create mode 100644 src/pmdas/lustrecomm/timespec_routines.c create mode 100644 src/pmdas/mailq/GNUmakefile create mode 100644 src/pmdas/mailq/Install create mode 100644 src/pmdas/mailq/README create mode 100644 src/pmdas/mailq/Remove create mode 100644 src/pmdas/mailq/help create mode 100644 src/pmdas/mailq/mailq.c create mode 100644 src/pmdas/mailq/pmlogconf.summary create mode 100644 src/pmdas/mailq/pmns create mode 100644 src/pmdas/mailq/root create mode 100644 src/pmdas/memcache/GNUmakefile create mode 100755 src/pmdas/memcache/Install create mode 100755 src/pmdas/memcache/Remove create mode 100755 src/pmdas/memcache/client.pl create mode 100644 src/pmdas/memcache/pmdamemcache.pl create mode 100644 src/pmdas/mmv/GNUmakefile create mode 100644 src/pmdas/mmv/Makefile.demos create mode 100644 src/pmdas/mmv/README.demos create mode 100644 src/pmdas/mmv/acme.c create mode 100644 src/pmdas/mmv/mmvdump.c create mode 100644 src/pmdas/mmv/src/GNUmakefile create mode 100755 src/pmdas/mmv/src/Install create mode 100755 src/pmdas/mmv/src/Remove create mode 100644 src/pmdas/mmv/src/mmv.c create mode 100644 src/pmdas/mmv/src/root_mmv create mode 100644 src/pmdas/mounts/GNUmakefile create mode 100755 src/pmdas/mounts/Install create mode 100644 src/pmdas/mounts/README create mode 100755 src/pmdas/mounts/Remove create mode 100644 src/pmdas/mounts/help create mode 100644 src/pmdas/mounts/mounts.c create mode 100644 src/pmdas/mounts/mounts.conf create mode 100644 src/pmdas/mounts/pmns create mode 100644 src/pmdas/mounts/root create mode 100644 src/pmdas/mssql/GNUmakefile create mode 100644 src/pmdas/mssql/Install create mode 100644 src/pmdas/mssql/Remove create mode 100644 src/pmdas/mssql/pmdamssql.pl create mode 100644 src/pmdas/mysql/GNUmakefile create mode 100755 src/pmdas/mysql/Install create mode 100644 src/pmdas/mysql/README create mode 100755 src/pmdas/mysql/Remove create mode 100644 src/pmdas/mysql/migrate.conf create mode 100644 src/pmdas/mysql/pmdamysql.pl create mode 100644 src/pmdas/mysql/pmlogconf.summary create mode 100644 src/pmdas/named/GNUmakefile create mode 100755 src/pmdas/named/Install create mode 100755 src/pmdas/named/Remove create mode 100644 src/pmdas/named/pmdanamed.pl create mode 100644 src/pmdas/netbsd/GNUmakefile create mode 100644 src/pmdas/netbsd/disk.c create mode 100644 src/pmdas/netbsd/help create mode 100644 src/pmdas/netbsd/netbsd.c create mode 100644 src/pmdas/netbsd/netbsd.h create mode 100644 src/pmdas/netbsd/netif.c create mode 100644 src/pmdas/netbsd/root_netbsd create mode 100644 src/pmdas/netfilter/GNUmakefile create mode 100755 src/pmdas/netfilter/Install create mode 100755 src/pmdas/netfilter/Remove create mode 100644 src/pmdas/netfilter/pmdanetfilter.pl create mode 100644 src/pmdas/netfilter/pmlogconf.config create mode 100644 src/pmdas/netfilter/pmlogconf.summary create mode 100644 src/pmdas/news/GNUmakefile create mode 100644 src/pmdas/news/Install create mode 100644 src/pmdas/news/README create mode 100644 src/pmdas/news/Remove create mode 100644 src/pmdas/news/active create mode 100644 src/pmdas/news/pmdanews.pl create mode 100644 src/pmdas/nfsclient/GNUmakefile create mode 100755 src/pmdas/nfsclient/Install create mode 100755 src/pmdas/nfsclient/Remove create mode 100644 src/pmdas/nfsclient/pmdanfsclient.pl create mode 100644 src/pmdas/nginx/GNUmakefile create mode 100755 src/pmdas/nginx/Install create mode 100755 src/pmdas/nginx/Remove create mode 100644 src/pmdas/nginx/nginx.conf create mode 100755 src/pmdas/nginx/pmdanginx.pl create mode 100644 src/pmdas/nvidia/GNUmakefile create mode 100755 src/pmdas/nvidia/Install create mode 100755 src/pmdas/nvidia/README create mode 100755 src/pmdas/nvidia/Remove create mode 100644 src/pmdas/nvidia/help create mode 100644 src/pmdas/nvidia/localnvml.c create mode 100644 src/pmdas/nvidia/localnvml.h create mode 100644 src/pmdas/nvidia/nvidia.c create mode 100644 src/pmdas/nvidia/pmns create mode 100644 src/pmdas/nvidia/root create mode 100644 src/pmdas/oracle/GNUmakefile create mode 100755 src/pmdas/oracle/Install create mode 100755 src/pmdas/oracle/Remove create mode 100644 src/pmdas/oracle/pmdaoracle.pl create mode 100644 src/pmdas/papi/GNUmakefile create mode 100644 src/pmdas/papi/Install create mode 100644 src/pmdas/papi/README create mode 100755 src/pmdas/papi/Remove create mode 100644 src/pmdas/papi/help create mode 100644 src/pmdas/papi/papi.c create mode 100644 src/pmdas/papi/pmns create mode 100644 src/pmdas/papi/root create mode 100644 src/pmdas/pdns/GNUmakefile create mode 100644 src/pmdas/pdns/Install create mode 100644 src/pmdas/pdns/Remove create mode 100644 src/pmdas/pdns/pmdapdns.pl create mode 100644 src/pmdas/pmcd/GNUmakefile create mode 100644 src/pmdas/pmcd/help create mode 100644 src/pmdas/pmcd/root_pmcd create mode 100644 src/pmdas/pmcd/src/GNUmakefile create mode 100755 src/pmdas/pmcd/src/objstyle create mode 100644 src/pmdas/pmcd/src/pmcd.c create mode 100644 src/pmdas/postfix/GNUmakefile create mode 100644 src/pmdas/postfix/Install create mode 100644 src/pmdas/postfix/Remove create mode 100644 src/pmdas/postfix/pmdapostfix.pl create mode 100644 src/pmdas/postgresql/GNUmakefile create mode 100755 src/pmdas/postgresql/Install create mode 100755 src/pmdas/postgresql/Remove create mode 100644 src/pmdas/postgresql/pmdapostgresql.pl create mode 100644 src/pmdas/postgresql/pmlogconf.summary create mode 100644 src/pmdas/process/GNUmakefile create mode 100755 src/pmdas/process/Install create mode 100644 src/pmdas/process/README create mode 100755 src/pmdas/process/Remove create mode 100644 src/pmdas/process/help create mode 100644 src/pmdas/process/pmns create mode 100644 src/pmdas/process/process.c create mode 100644 src/pmdas/process/process.conf create mode 100644 src/pmdas/process/root create mode 100644 src/pmdas/roomtemp/GNUmakefile create mode 100644 src/pmdas/roomtemp/Install create mode 100644 src/pmdas/roomtemp/README create mode 100644 src/pmdas/roomtemp/Remove create mode 100644 src/pmdas/roomtemp/dsread.c create mode 100644 src/pmdas/roomtemp/dsread.h create mode 100644 src/pmdas/roomtemp/help create mode 100644 src/pmdas/roomtemp/mlan/GNUmakefile create mode 100644 src/pmdas/roomtemp/mlan/ds2480.h create mode 100644 src/pmdas/roomtemp/mlan/ds2480ut.c create mode 100644 src/pmdas/roomtemp/mlan/linuxlnk.c create mode 100644 src/pmdas/roomtemp/mlan/mlan.h create mode 100644 src/pmdas/roomtemp/mlan/mlanllu.c create mode 100644 src/pmdas/roomtemp/mlan/mlannetu.c create mode 100644 src/pmdas/roomtemp/mlan/mlansesu.c create mode 100644 src/pmdas/roomtemp/mlan/mlantrnu.c create mode 100644 src/pmdas/roomtemp/pmns create mode 100644 src/pmdas/roomtemp/roomtemp.c create mode 100644 src/pmdas/roomtemp/root create mode 100644 src/pmdas/rpm/GNUmakefile create mode 100644 src/pmdas/rpm/Install create mode 100644 src/pmdas/rpm/Remove create mode 100644 src/pmdas/rpm/help create mode 100644 src/pmdas/rpm/migrate.conf create mode 100644 src/pmdas/rpm/pmns create mode 100644 src/pmdas/rpm/root create mode 100644 src/pmdas/rpm/rpm.c create mode 100644 src/pmdas/rpm/rpm.h create mode 100644 src/pmdas/rpm/timer.c create mode 100644 src/pmdas/rpm/timer.h create mode 100644 src/pmdas/rsyslog/GNUmakefile create mode 100755 src/pmdas/rsyslog/Install create mode 100755 src/pmdas/rsyslog/Remove create mode 100644 src/pmdas/rsyslog/pmdarsyslog.pl create mode 100644 src/pmdas/samba/GNUmakefile create mode 100755 src/pmdas/samba/Install create mode 100755 src/pmdas/samba/Remove create mode 100644 src/pmdas/samba/pmdasamba.pl create mode 100644 src/pmdas/sample/GNUmakefile create mode 100755 src/pmdas/sample/Install create mode 100644 src/pmdas/sample/README create mode 100755 src/pmdas/sample/Remove create mode 100644 src/pmdas/sample/Sample.pmchart create mode 100644 src/pmdas/sample/domain.h create mode 100755 src/pmdas/sample/get_next_pmid create mode 100644 src/pmdas/sample/help create mode 100644 src/pmdas/sample/pmns create mode 100644 src/pmdas/sample/root create mode 100644 src/pmdas/sample/src/GNUmakefile create mode 100644 src/pmdas/sample/src/GNUmakefile.install create mode 100644 src/pmdas/sample/src/events.c create mode 100644 src/pmdas/sample/src/events.h create mode 100644 src/pmdas/sample/src/percontext.c create mode 100644 src/pmdas/sample/src/percontext.h create mode 100644 src/pmdas/sample/src/pmda.c create mode 100644 src/pmdas/sample/src/sample.c create mode 100644 src/pmdas/sendmail/GNUmakefile create mode 100644 src/pmdas/sendmail/Install create mode 100644 src/pmdas/sendmail/README create mode 100644 src/pmdas/sendmail/Remove create mode 100644 src/pmdas/sendmail/Sendmail.pmchart create mode 100644 src/pmdas/sendmail/help create mode 100644 src/pmdas/sendmail/pmns create mode 100644 src/pmdas/sendmail/root create mode 100644 src/pmdas/sendmail/sendmail.c create mode 100644 src/pmdas/shping/GNUmakefile create mode 100755 src/pmdas/shping/Install create mode 100644 src/pmdas/shping/README create mode 100644 src/pmdas/shping/Remove create mode 100644 src/pmdas/shping/help create mode 100644 src/pmdas/shping/pmda.c create mode 100644 src/pmdas/shping/pmlogconf.summary create mode 100644 src/pmdas/shping/pmns create mode 100644 src/pmdas/shping/root create mode 100644 src/pmdas/shping/sample.conf create mode 100644 src/pmdas/shping/shping.CPUTime.pmchart create mode 100644 src/pmdas/shping/shping.RealTime.pmchart create mode 100644 src/pmdas/shping/shping.c create mode 100644 src/pmdas/shping/shping.h create mode 100644 src/pmdas/shping/shping.response.pmie create mode 100644 src/pmdas/shping/shping.status.pmie create mode 100644 src/pmdas/simple/GNUmakefile create mode 100644 src/pmdas/simple/GNUmakefile.install create mode 100644 src/pmdas/simple/Install create mode 100644 src/pmdas/simple/README create mode 100644 src/pmdas/simple/Remove create mode 100644 src/pmdas/simple/help create mode 100644 src/pmdas/simple/pmdasimple.perl create mode 100644 src/pmdas/simple/pmdasimple.python create mode 100644 src/pmdas/simple/pmns create mode 100644 src/pmdas/simple/root create mode 100644 src/pmdas/simple/simple.c create mode 100644 src/pmdas/simple/simple.conf create mode 100644 src/pmdas/snmp/GNUmakefile create mode 100755 src/pmdas/snmp/Install create mode 100644 src/pmdas/snmp/README create mode 100755 src/pmdas/snmp/Remove create mode 100755 src/pmdas/snmp/pmdasnmp.pl create mode 100644 src/pmdas/snmp/snmp.conf create mode 100644 src/pmdas/solaris/GNUmakefile create mode 100644 src/pmdas/solaris/arcstats.c create mode 100644 src/pmdas/solaris/clusters.h create mode 100644 src/pmdas/solaris/common.h create mode 100644 src/pmdas/solaris/data.c create mode 100644 src/pmdas/solaris/disk.c create mode 100644 src/pmdas/solaris/help create mode 100644 src/pmdas/solaris/kvm.c create mode 100644 src/pmdas/solaris/netlink.c create mode 100644 src/pmdas/solaris/netmib2.c create mode 100644 src/pmdas/solaris/netmib2.h create mode 100644 src/pmdas/solaris/pmns.disk create mode 100644 src/pmdas/solaris/pmns.hinv create mode 100644 src/pmdas/solaris/pmns.kernel create mode 100644 src/pmdas/solaris/pmns.mem create mode 100644 src/pmdas/solaris/pmns.network create mode 100644 src/pmdas/solaris/pmns.zfs create mode 100644 src/pmdas/solaris/pmns.zpool create mode 100644 src/pmdas/solaris/pmns.zpool_perdisk create mode 100644 src/pmdas/solaris/root create mode 100644 src/pmdas/solaris/solaris.c create mode 100644 src/pmdas/solaris/sysinfo.c create mode 100644 src/pmdas/solaris/vnops.c create mode 100644 src/pmdas/solaris/zfs.c create mode 100644 src/pmdas/solaris/zpool.c create mode 100644 src/pmdas/solaris/zpool_perdisk.c create mode 100644 src/pmdas/summary/GNUmakefile create mode 100644 src/pmdas/summary/Install create mode 100644 src/pmdas/summary/README create mode 100644 src/pmdas/summary/Remove create mode 100644 src/pmdas/summary/help create mode 100644 src/pmdas/summary/mainloop.c create mode 100644 src/pmdas/summary/pmda.c create mode 100644 src/pmdas/summary/pmns create mode 100644 src/pmdas/summary/root create mode 100644 src/pmdas/summary/summary.c create mode 100644 src/pmdas/summary/summary.h create mode 100644 src/pmdas/summary/summary.pmie create mode 100644 src/pmdas/systemd/GNUmakefile create mode 100755 src/pmdas/systemd/Install create mode 100644 src/pmdas/systemd/README create mode 100755 src/pmdas/systemd/Remove create mode 100644 src/pmdas/systemd/help create mode 100644 src/pmdas/systemd/pmns create mode 100644 src/pmdas/systemd/root create mode 100644 src/pmdas/systemd/systemd.c create mode 100644 src/pmdas/systemtap/GNUmakefile create mode 100755 src/pmdas/systemtap/Install create mode 100644 src/pmdas/systemtap/README create mode 100755 src/pmdas/systemtap/Remove create mode 100644 src/pmdas/systemtap/pmdasystemtap.pl create mode 100644 src/pmdas/systemtap/probes.stp create mode 100644 src/pmdas/trace/GNUmakefile create mode 100644 src/pmdas/trace/GNUmakefile.stub create mode 100644 src/pmdas/trace/Install create mode 100644 src/pmdas/trace/Makefile.proto create mode 100644 src/pmdas/trace/README create mode 100644 src/pmdas/trace/README.demos create mode 100644 src/pmdas/trace/Remove create mode 100644 src/pmdas/trace/app1.c create mode 100644 src/pmdas/trace/app2.c create mode 100644 src/pmdas/trace/app3.c create mode 100644 src/pmdas/trace/fapp1.f create mode 100644 src/pmdas/trace/help create mode 100644 src/pmdas/trace/japp1.java create mode 100644 src/pmdas/trace/pmns create mode 100644 src/pmdas/trace/root create mode 100644 src/pmdas/trace/src/GNUmakefile create mode 100644 src/pmdas/trace/src/client.c create mode 100644 src/pmdas/trace/src/client.h create mode 100644 src/pmdas/trace/src/comms.c create mode 100644 src/pmdas/trace/src/comms.h create mode 100644 src/pmdas/trace/src/data.c create mode 100644 src/pmdas/trace/src/data.h create mode 100644 src/pmdas/trace/src/pmda.c create mode 100644 src/pmdas/trace/src/trace.c create mode 100644 src/pmdas/trace/stub.c create mode 100644 src/pmdas/trivial/GNUmakefile create mode 100644 src/pmdas/trivial/GNUmakefile.install create mode 100644 src/pmdas/trivial/Install create mode 100644 src/pmdas/trivial/README create mode 100644 src/pmdas/trivial/Remove create mode 100644 src/pmdas/trivial/help create mode 100644 src/pmdas/trivial/pmns create mode 100644 src/pmdas/trivial/root create mode 100644 src/pmdas/trivial/trivial.c create mode 100644 src/pmdas/txmon/GNUmakefile create mode 100644 src/pmdas/txmon/GNUmakefile.install create mode 100755 src/pmdas/txmon/Install create mode 100644 src/pmdas/txmon/README create mode 100644 src/pmdas/txmon/Remove create mode 100755 src/pmdas/txmon/genload create mode 100644 src/pmdas/txmon/help create mode 100644 src/pmdas/txmon/pmns create mode 100644 src/pmdas/txmon/root create mode 100644 src/pmdas/txmon/txmon.c create mode 100644 src/pmdas/txmon/txmon.h create mode 100644 src/pmdas/txmon/txrecord.c create mode 100644 src/pmdas/vmware/GNUmakefile create mode 100755 src/pmdas/vmware/Install create mode 100755 src/pmdas/vmware/Remove create mode 100644 src/pmdas/vmware/pmdavmware.pl create mode 100644 src/pmdas/weblog/GNUmakefile create mode 100644 src/pmdas/weblog/Install create mode 100644 src/pmdas/weblog/README create mode 100644 src/pmdas/weblog/Remove create mode 100644 src/pmdas/weblog/Web.Alarms.pmchart create mode 100755 src/pmdas/weblog/Web.Allservers.pmchart create mode 100755 src/pmdas/weblog/Web.Perserver.Bytes.pmchart create mode 100755 src/pmdas/weblog/Web.Perserver.Requests.pmchart create mode 100644 src/pmdas/weblog/Web.Requests.pmchart create mode 100644 src/pmdas/weblog/Web.Volume.pmchart create mode 100644 src/pmdas/weblog/check_match.c create mode 100644 src/pmdas/weblog/help create mode 100644 src/pmdas/weblog/pmda.c create mode 100644 src/pmdas/weblog/pmns create mode 100644 src/pmdas/weblog/root create mode 100755 src/pmdas/weblog/server.sh create mode 100644 src/pmdas/weblog/sproc.c create mode 100644 src/pmdas/weblog/weblog.c create mode 100644 src/pmdas/weblog/weblog.h create mode 100755 src/pmdas/weblog/weblogconv.sh create mode 100644 src/pmdas/windows/GNUmakefile create mode 100644 src/pmdas/windows/README create mode 100644 src/pmdas/windows/error.c create mode 100644 src/pmdas/windows/fetch.c create mode 100644 src/pmdas/windows/help create mode 100644 src/pmdas/windows/helptext.c create mode 100644 src/pmdas/windows/hypnotoad.h create mode 100644 src/pmdas/windows/instance.c create mode 100644 src/pmdas/windows/open.c create mode 100644 src/pmdas/windows/pdhlist.c create mode 100644 src/pmdas/windows/pdhmatch.sh create mode 100644 src/pmdas/windows/pmda.c create mode 100644 src/pmdas/windows/pmns.disk create mode 100644 src/pmdas/windows/pmns.filesys create mode 100644 src/pmdas/windows/pmns.hinv create mode 100644 src/pmdas/windows/pmns.kernel create mode 100644 src/pmdas/windows/pmns.mem create mode 100644 src/pmdas/windows/pmns.network create mode 100644 src/pmdas/windows/pmns.pmda create mode 100644 src/pmdas/windows/pmns.process create mode 100644 src/pmdas/windows/pmns.sqlserver create mode 100644 src/pmdas/windows/root create mode 100644 src/pmdas/zimbra/GNUmakefile create mode 100755 src/pmdas/zimbra/Install create mode 100755 src/pmdas/zimbra/Remove create mode 100644 src/pmdas/zimbra/pmdazimbra.pl create mode 100644 src/pmdas/zimbra/pmlogconf.all create mode 100755 src/pmdas/zimbra/zimbraprobe.sh create mode 100644 src/pmdas/zswap/GNUmakefile create mode 100644 src/pmdas/zswap/Install create mode 100644 src/pmdas/zswap/Remove create mode 100644 src/pmdas/zswap/pmdazswap.python (limited to 'src/pmdas') diff --git a/src/pmdas/GNUmakefile b/src/pmdas/GNUmakefile new file mode 100644 index 0000000..f809ef0 --- /dev/null +++ b/src/pmdas/GNUmakefile @@ -0,0 +1,61 @@ +# +# Copyright (c) 2013-2014 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../.. +include $(TOPDIR)/src/include/builddefs +-include ./GNUlocaldefs + +CPMDAS = pmcd linux darwin freebsd solaris aix windows \ + sample simple trivial sendmail mailq txmon \ + cisco trace apache shping mounts weblog \ + lmsensors process roomtemp summary hotproc \ + lustrecomm linux_proc mmv etw logger bash \ + systemd gfs2 netbsd linux_xfs infiniband jbd2 \ + rpm nvidia papi + +PLPMDAS = bonding netfilter zimbra postgresql \ + dbping memcache systemtap mysql vmware kvm \ + named news pdns samba dtsrun postfix gpsd \ + rsyslog elasticsearch snmp nginx nfsclient + +PYPMDAS = gluster zswap dmcache + +SUBDIRS = $(CPMDAS) $(PLPMDAS) $(PYPMDAS) +LDIRT = pmcd.conf + +default :: default_pcp + +default_pcp : $(SUBDIRS) + @echo '# Performance Metrics Domain Specifications' > pmcd.conf + @echo '# ' >> pmcd.conf + @echo '# This file is automatically generated during the build' >> pmcd.conf + @echo '# Name Id IPC IPC Params File/Cmd' >> pmcd.conf + $(SUBDIRS_MAKERULE) + @echo >> pmcd.conf + @echo '[access]' >> pmcd.conf + @echo 'disallow ".*" : store;' >> pmcd.conf + @echo 'disallow ":*" : store;' >> pmcd.conf + @echo 'allow "local:*" : all;' >> pmcd.conf + +install :: install_pcp + +install_pcp :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install_pcp :: default_pcp + $(INSTALL) -m 755 -d `dirname $(PCP_PMCDCONF_PATH)` + $(INSTALL) -m 644 pmcd.conf $(PCP_PMCDCONF_PATH) + +include $(BUILDRULES) diff --git a/src/pmdas/aix/GNUmakefile b/src/pmdas/aix/GNUmakefile new file mode 100644 index 0000000..9282d76 --- /dev/null +++ b/src/pmdas/aix/GNUmakefile @@ -0,0 +1,69 @@ +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = aix +DOMAIN = AIX +CMDTARGET = pmdaaix +LIBTARGET = pmda_aix.so +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "aix 80 dso aix_init $(PMDADIR)/$(LIBTARGET)" + +CFILES = aix.c data.c disk_total.c disk.c cpu_total.c cpu.c netif.c + +LLDLIBS = $(PCP_PMDALIB) -lperfstat +PMNS = pmns.disk pmns.kernel pmns.mem pmns.network pmns.hinv +LSRCFILES = $(PMNS) help root common.h + +LDIRT = *.log *.dir *.pag root_aix domain.h + +default: build-me + +ifeq "$(TARGET_OS)" "aix" +build-me: common.h root_aix domain.h $(CMDTARGET) $(LIBTARGET) help.dir help.pag + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help.dir help.pag $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root_aix $(PCP_VAR_DIR)/pmns/root_aix +else +build-me: +install: +endif + +$(OBJECTS): common.h + +include $(BUILDRULES) + +default_pcp : default + +install_pcp : install + +help.dir help.pag: help root_aix + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_aix -v 2 -o help < help + +root_aix: ../../pmns/stdpmid + rm -f root_aix + sed -e 's;;"../../pmns/stdpmid";' root_aix + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/aix/aix.c b/src/pmdas/aix/aix.c new file mode 100644 index 0000000..335aacd --- /dev/null +++ b/src/pmdas/aix/aix.c @@ -0,0 +1,127 @@ +/* + * AIX PMDA + * + * Collect performance data from the AIX kernel using libperfstat for + * the most part. + * + * Copyright (c) 2012,2014 Red Hat. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "common.h" + +static int _isDSO = 1; +static char mypath[MAXPATHLEN]; +static char *username; + +/* + * wrapper for pmdaFetch which primes the methods ready for + * the next fetch + * ... real callback is fetch_callback() + */ +static int +aix_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; + + // TODO: this should only fetch metrics from "pmidlist" + for (i = 0; i < methodtab_sz; i++) { + methodtab[i].m_prefetch(); + } + + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * callback provided to pmdaFetch + */ +static int +aix_fetch_callback(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + metricdesc_t *mdp; + + mdp = (metricdesc_t *)mdesc->m_user; + return methodtab[mdp->md_method].m_fetch(mdesc, inst, atom); +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +aix_init(pmdaInterface *dp) +{ + if (_isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "aix" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_3, "AIX DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.two.fetch = aix_fetch; + pmdaSetFetchCallBack(dp, aix_fetch_callback); + init_data(dp->domain); + pmdaInit(dp, indomtab, indomtab_sz, metrictab, metrictab_sz); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface desc; + + _isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "aix" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_3, pmProgname, AIX, "aix.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&desc); + aix_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + exit(0); +} diff --git a/src/pmdas/aix/common.h b/src/pmdas/aix/common.h new file mode 100644 index 0000000..604ff73 --- /dev/null +++ b/src/pmdas/aix/common.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "./domain.h" + +typedef long long longlong_t; // TODO nuke for AIX +typedef unsigned long long u_longlong_t; // TODO nuke for AIX build +typedef unsigned char uchar; // TODO nuke for AIX build + +#include + +/* + * libperfstat method controls + * + * md_method choices (see below) ... must be contiguous integers so + * we can index directly into method[] + */ +#define M_CPU_TOTAL 0 +#define M_CPU 1 +#define M_DISK_TOTAL 2 +#define M_DISK 3 +#define M_NETIF 4 +#define M_NETBUF 5 // TODO +#define M_PROTO 6 // TODO +#define M_MEM_TOTAL 7 // TODO + +/* + * special values for offset + */ +#define OFF_NOVALUES -2 +#define OFF_DERIVED -1 + +typedef struct { + void (*m_init)(int); + void (*m_prefetch)(void); + int (*m_fetch)(pmdaMetric *, int, pmAtomValue *); +} method_t; + +extern method_t methodtab[]; +extern int methodtab_sz; + +extern void init_data(int); + +extern void cpu_total_init(int); +extern void cpu_total_prefetch(void); +extern int cpu_total_fetch(pmdaMetric *, int, pmAtomValue *); + +extern void cpu_init(int); +extern void cpu_prefetch(void); +extern int cpu_fetch(pmdaMetric *, int, pmAtomValue *); + +extern void disk_total_init(int); +extern void disk_total_prefetch(void); +extern int disk_total_fetch(pmdaMetric *, int, pmAtomValue *); + +extern void disk_init(int); +extern void disk_prefetch(void); +extern int disk_fetch(pmdaMetric *, int, pmAtomValue *); + +extern void netif_init(int); +extern void netif_prefetch(void); +extern int netif_fetch(pmdaMetric *, int, pmAtomValue *); + +/* + * metric descriptions + */ +typedef struct { + pmDesc md_desc; // PMDA's idea of the semantics + int md_method; // specific stats method + int md_offset; // offset into stats structure +} metricdesc_t; + +extern metricdesc_t metricdesc[]; +extern pmdaMetric *metrictab; +extern int metrictab_sz; + +#define DISK_INDOM 0 +#define CPU_INDOM 1 +#define NETIF_INDOM 2 + +extern pmdaIndom indomtab[]; +extern int indomtab_sz; diff --git a/src/pmdas/aix/cpu.c b/src/pmdas/aix/cpu.c new file mode 100644 index 0000000..98d3348 --- /dev/null +++ b/src/pmdas/aix/cpu.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" + +static int ncpu; +static int ncpu_alloc; +static int *fetched; +static perfstat_cpu_t *cpustat; + +void +cpu_init(int first) +{ + perfstat_id_t id; + int i; + + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; + + ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0); + if ((fetched = (int *)malloc(ncpu * sizeof(int))) == NULL) { + fprintf(stderr, "cpu_init: fetched malloc[%d] failed: %s\n", + ncpu * sizeof(int), osstrerror()); + exit(1); + } + if ((cpustat = (perfstat_cpu_t *)malloc(ncpu * sizeof(perfstat_cpu_t))) == NULL) { + fprintf(stderr, "cpu_init: cpustat malloc[%d] failed: %s\n", + ncpu * sizeof(perfstat_cpu_t), osstrerror()); + exit(1); + } + ncpu_alloc = ncpu; + + /* + * set up instance domain + */ + strcpy(id.name, ""); + ncpu = perfstat_cpu(&id, cpustat, sizeof(perfstat_cpu_t), ncpu_alloc); + + indomtab[CPU_INDOM].it_numinst = ncpu; + indomtab[CPU_INDOM].it_set = (pmdaInstid *)malloc(ncpu * sizeof(pmdaInstid)); + if (indomtab[CPU_INDOM].it_set == NULL) { + fprintf(stderr, "cpu_init: indomtab malloc[%d] failed: %s\n", + ncpu * sizeof(pmdaInstid), osstrerror()); + exit(1); + } + for (i = 0; i < ncpu; i++) { + indomtab[CPU_INDOM].it_set[i].i_inst = i; + indomtab[CPU_INDOM].it_set[i].i_name = strdup(cpustat[i].name); + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_init: ncpu=%d\n", ncpu); + } +#endif +} + +void +cpu_prefetch(void) +{ + int i; + + for (i = 0; i < ncpu_alloc; i++) + fetched[i] = 0; +} + +static __uint64_t +cpu_derived(pmdaMetric *mdesc, int inst) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + + switch (pmid) { + + default: + fprintf(stderr, "cpu_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, val); + } +#endif + + return val; +} + +int +cpu_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int offset; + + if (fetched[inst] == 0) { + int sts; + int i; + perfstat_id_t id; + + strcpy(id.name, ""); + sts = perfstat_cpu(&id, cpustat, sizeof(perfstat_cpu_t), ncpu_alloc); + + /* TODO ... + * - if sts != ncpu, the number of CPUs has changed, need to set + * fetched[i] to -1 for the missing ones + * - is sts > ncpu possible? worse, if the number of cpus is > + * ncpu_alloc what should we do? + * - possibly reshape the instance domain? + * - error handling? + */ + + for (i = 0; i < ncpu; i++) { + fetched[i] = 1; + } + } + + if (fetched[inst] != 1) + return 0; + + offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + if (offset == OFF_NOVALUES) + return 0; + + if (mdesc->m_desc.type == PM_TYPE_U64) { + if (offset == OFF_DERIVED) + atom->ull = cpu_derived(mdesc, inst); + else { + __uint64_t *ullp; + ullp = (__uint64_t *)&((char *)&cpustat[inst])[offset]; + atom->ull = *ullp; + } + if (mdesc->m_desc.units.scaleTime == PM_TIME_MSEC) { + /* assumed to be CPU time */ + atom->ull *= 1000 / HZ; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ull); + } +#endif + } + else { + if (offset == OFF_DERIVED) + atom->ul = (__uint32_t)cpu_derived(mdesc, inst); + else { + __uint32_t *ulp; + ulp = (__uint32_t *)&((char *)&cpustat[inst])[offset]; + atom->ul = *ulp; + } + if (mdesc->m_desc.units.scaleTime == PM_TIME_MSEC) { + /* assumed to be CPU time */ + atom->ul *= 1000 / HZ; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_fetch: pmid %s inst %d val %lu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ul); + } +#endif + } + + return 1; +} diff --git a/src/pmdas/aix/cpu_total.c b/src/pmdas/aix/cpu_total.c new file mode 100644 index 0000000..bf3d118 --- /dev/null +++ b/src/pmdas/aix/cpu_total.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" +#include + +static perfstat_cpu_total_t cpustat; +static int fetched; + +void +cpu_total_init(int first) +{ + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; +} + +void +cpu_total_prefetch(void) +{ + int i; + + fetched = 0; +} + +static __uint64_t +cpu_total_derived(pmdaMetric *mdesc, int inst) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + + switch (pmid) { + + default: + fprintf(stderr, "cpu_total_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_total_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, val); + } +#endif + + return val; +} + +int +cpu_total_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int offset; + + if (fetched == 0) { + int sts; + sts = perfstat_cpu_total(NULL, &cpustat, sizeof(perfstat_cpu_total_t), 1); + if (sts != 1) { + /* TODO - how to find/decode errors? */ + fprintf(stderr, "perfstat_cpu_total: failed %s\n", osstrerror()); + fetched = -1; + } + else + fetched = 1; + } + + if (fetched != 1) + return 0; + + offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + if (offset == OFF_NOVALUES) + return 0; + + if (mdesc->m_desc.type == PM_TYPE_U64) { + if (offset == OFF_DERIVED) + atom->ull = cpu_total_derived(mdesc, inst); + else { + __uint64_t *ullp; + ullp = (__uint64_t *)&((char *)&cpustat)[offset]; + atom->ull = *ullp; + } + if (mdesc->m_desc.units.scaleTime == PM_TIME_MSEC) { + /* assumed to be CPU time */ + atom->ull *= 1000 / HZ; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_total_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ull); + } +#endif + } + else { + if (offset == OFF_DERIVED) + atom->ul = (__uint32_t)cpu_total_derived(mdesc, inst); + else { + __uint32_t *ulp; + ulp = (__uint32_t *)&((char *)&cpustat)[offset]; + atom->ul = *ulp; + } + if (mdesc->m_desc.units.scaleTime == PM_TIME_MSEC) { + /* assumed to be CPU time */ + atom->ul *= 1000 / HZ; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_total_fetch: pmid %s inst %d val %lu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ul); + } +#endif + } + + return 1; +} diff --git a/src/pmdas/aix/data.c b/src/pmdas/aix/data.c new file mode 100644 index 0000000..6198e78 --- /dev/null +++ b/src/pmdas/aix/data.c @@ -0,0 +1,435 @@ +/* + * Data structures that define metrics and control the AIX PMDA + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" +#include + +/* + * List of instance domains ... we expect the *_INDOM macros + * to index into this table. + */ +pmdaIndom indomtab[] = { + { DISK_INDOM, 0, NULL }, + { CPU_INDOM, 0, NULL }, + { NETIF_INDOM, 0, NULL } +}; +int indomtab_sz = sizeof(indomtab) / sizeof(indomtab[0]); + +pmdaMetric *metrictab; + +method_t methodtab[] = { + { cpu_total_init, cpu_total_prefetch, cpu_total_fetch }, // M_CPU_TOTAL + { cpu_init, cpu_prefetch, cpu_fetch }, // M_CPU + { disk_total_init, disk_total_prefetch, disk_total_fetch }, // M_DISK_TOTAL + { disk_init, disk_prefetch, disk_fetch }, // M_DISK + { netif_init, netif_prefetch, netif_fetch } // M_NETIF + // M_NETBUF - TODO + // M_PROTO - TODO + // M_MEM_TOTAL - TODO +}; +int methodtab_sz = sizeof(methodtab) / sizeof(methodtab[0]); + +#define CPU_OFF(field) ((int)&((perfstat_cpu_t *)0)->field) +#define CPU_TOTAL_OFF(field) ((int)&((perfstat_cpu_total_t *)0)->field) +#define DISK_OFF(field) ((int)&((perfstat_disk_t *)0)->field) +#define DISK_TOTAL_OFF(field) ((int)&((perfstat_disk_total_t *)0)->field) +#define NETIF_OFF(field) ((int)&((perfstat_netinterface_t *)0)->field) + +/* + * all metrics supported in this PMDA - one table entry for each metric + */ +metricdesc_t metricdesc[] = { + +/* kernel.all.cpu.idle */ + { { PMDA_PMID(0,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(idle) }, + +/* kernel.all.cpu.user */ + { { PMDA_PMID(0,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(user) }, + +/* kernel.all.cpu.sys */ + { { PMDA_PMID(0,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(sys) }, + +/* kernel.all.cpu.wait.total */ + { { PMDA_PMID(0,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(wait) }, + +/* kernel.percpu.cpu.idle */ + { { PMDA_PMID(0,4), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, CPU_OFF(idle) }, + +/* kernel.percpu.cpu.user */ + { { PMDA_PMID(0,5), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, CPU_OFF(user) }, + +/* kernel.percpu.cpu.sys */ + { { PMDA_PMID(0,6), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, CPU_OFF(sys) }, + +/* kernel.percpu.cpu.wait.total */ + { { PMDA_PMID(0,7), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, CPU_OFF(wait) }, + +/* kernel.all.readch */ + { { PMDA_PMID(0,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(readch) }, + +/* kernel.all.writech */ + { { PMDA_PMID(0,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(readch) }, + +/* kernel.all.io.softintrs */ + { { PMDA_PMID(0,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(softintrs) }, + +/* kernel.percpu.readch */ + { { PMDA_PMID(0,11), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, CPU_OFF(readch) }, + +/* kernel.percpu.writech */ + { { PMDA_PMID(0,12), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, CPU_OFF(writech) }, + +/* kernel.percpu.cpu.intr */ + { { PMDA_PMID(0,13), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU, OFF_NOVALUES }, + +/* kernel.all.io.bread */ + { { PMDA_PMID(0,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(bread) }, + +/* kernel.all.io.bwrite */ + { { PMDA_PMID(0,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(bwrite) }, + +/* kernel.all.io.lread */ + { { PMDA_PMID(0,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(lread) }, + +/* kernel.all.io.lwrite */ + { { PMDA_PMID(0,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(lwrite) }, + +/* kernel.percpu.io.bread */ + { { PMDA_PMID(0,18), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(bread) }, + +/* kernel.percpu.io.bwrite */ + { { PMDA_PMID(0,19), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(bwrite) }, + +/* kernel.percpu.io.lread */ + { { PMDA_PMID(0,20), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(lread) }, + +/* kernel.percpu.io.lwrite */ + { { PMDA_PMID(0,21), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(lwrite) }, + +/* kernel.all.syscall */ + { { PMDA_PMID(0,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(syscall) }, + +/* kernel.all.pswitch */ + { { PMDA_PMID(0,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(pswitch) }, + +/* kernel.percpu.syscall */ + { { PMDA_PMID(0,24), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(syscall) }, + +/* kernel.percpu.pswitch */ + { { PMDA_PMID(0,25), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(pswitch) }, + +/* kernel.all.io.phread */ + { { PMDA_PMID(0,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(phread) }, + +/* kernel.all.io.phwrite */ + { { PMDA_PMID(0,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(phwrite) }, + +/* kernel.all.io.devintrs */ + { { PMDA_PMID(0,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(devintrs) }, + +/* kernel.percpu.io.phread */ + { { PMDA_PMID(0,29), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(phread) }, + +/* kernel.percpu.io.phwrite */ + { { PMDA_PMID(0,30), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(phwrite) }, + +/* kernel.all.cpu.intr */ + { { PMDA_PMID(0,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_CPU_TOTAL, OFF_NOVALUES }, + +/* kernel.all.trap */ + { { PMDA_PMID(0,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(traps) }, + +/* kernel.all.sysexec */ + { { PMDA_PMID(0,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(sysexec) }, + +/* kernel.all.sysfork */ + { { PMDA_PMID(0,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(sysfork) }, + +/* kernel.all.io.namei */ + { { PMDA_PMID(0,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(namei) }, + +/* kernel.all.sysread */ + { { PMDA_PMID(0,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(sysread) }, + +/* kernel.all.syswrite */ + { { PMDA_PMID(0,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(syswrite) }, + +/* hinv.ncpu_cfg */ + { { PMDA_PMID(0,38), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(ncpus_cfg) }, + +/* kernel.percpu.sysexec */ + { { PMDA_PMID(0,39), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(sysexec) }, + +/* kernel.percpu.sysfork */ + { { PMDA_PMID(0,40), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(sysfork) }, + +/* kernel.percpu.io.namei */ + { { PMDA_PMID(0,41), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(namei) }, + +/* kernel.percpu.sysread */ + { { PMDA_PMID(0,42), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(sysread) }, + +/* kernel.percpu.syswrite */ + { { PMDA_PMID(0,43), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU, CPU_OFF(syswrite) }, + +/* disk.all.read -- not available */ + { { PMDA_PMID(0,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK_TOTAL, OFF_NOVALUES }, + +/* disk.all.write -- not available */ + { { PMDA_PMID(0,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK_TOTAL, OFF_NOVALUES }, + +/* disk.all.total */ + { { PMDA_PMID(0,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK_TOTAL, DISK_TOTAL_OFF(xfers) }, + +/* disk.all.read_bytes */ + { { PMDA_PMID(0,47), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, M_DISK_TOTAL, DISK_TOTAL_OFF(rblks) }, + +/* disk.all.write_bytes */ + { { PMDA_PMID(0,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, M_DISK_TOTAL, DISK_TOTAL_OFF(wblks) }, + +/* disk.all.total_bytes */ + { { PMDA_PMID(0,49), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, M_DISK_TOTAL, OFF_DERIVED }, + +/* disk.dev.read -- not available */ + { { PMDA_PMID(0,50), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK, OFF_NOVALUES }, + +/* disk.dev.write -- not available */ + { { PMDA_PMID(0,51), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK, OFF_NOVALUES }, + +/* disk.dev.total */ + { { PMDA_PMID(0,52), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK, DISK_OFF(xfers) }, + +/* disk.dev.read_bytes */ + { { PMDA_PMID(0,53), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, M_DISK, DISK_OFF(rblks) }, + +/* disk.dev.write_bytes */ + { { PMDA_PMID(0,54), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, M_DISK, DISK_OFF(wblks) }, + +/* disk.dev.total_bytes */ + { { PMDA_PMID(0,55), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, M_DISK, OFF_DERIVED }, + +/* hinv.ncpu */ + { { PMDA_PMID(0,56), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_CPU_TOTAL, CPU_TOTAL_OFF(ncpus) }, + +/* hinv.ndisk */ + { { PMDA_PMID(0,57), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_DISK_TOTAL, DISK_TOTAL_OFF(number) }, + +/* hinv.nnetif */ + { { PMDA_PMID(0,58), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NETIF, OFF_DERIVED }, + +/* network.interface.in.packets */ + { { PMDA_PMID(0,59), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NETIF, NETIF_OFF(ipackets) }, + +/* network.interface.in.bytes */ + { { PMDA_PMID(0,60), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, M_NETIF, NETIF_OFF(ibytes) }, + +/* network.interface.in.errors */ + { { PMDA_PMID(0,61), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NETIF, NETIF_OFF(ierrors) }, + +/* network.interface.out.packets */ + { { PMDA_PMID(0,62), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NETIF, NETIF_OFF(opackets) }, + +/* network.interface.out.bytes */ + { { PMDA_PMID(0,63), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, M_NETIF, NETIF_OFF(obytes) }, + +/* network.interface.out.errors */ + { { PMDA_PMID(0,64), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NETIF, NETIF_OFF(oerrors) }, + +/* network.interface.total.packets */ + { { PMDA_PMID(0,65), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NETIF, OFF_DERIVED }, + +/* network.interface.total.bytes */ + { { PMDA_PMID(0,66), PM_TYPE_U64, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, M_NETIF, OFF_DERIVED } + +/* remember to add trailing comma before adding more entries ... */ +}; +int metrictab_sz = sizeof(metricdesc) / sizeof(metricdesc[0]); + +void +init_data(int domain) +{ + int i; + int serial; + __pmID_int *ip; + __pmInDom_int *iip; + + /* + * Create the PMDA's metrictab[] version of the per-metric table. + * + * Also do domain initialization for each pmid and indom element of + * the metricdesc[] table ... the PMDA table is fixed up in + * libpcp_pmda + */ + if ((metrictab = (pmdaMetric *)malloc(metrictab_sz * sizeof(pmdaMetric))) == NULL) { + fprintf(stderr, "init_data: Error: malloc metrictab [%d] failed: %s\n", + metrictab_sz * sizeof(pmdaMetric), osstrerror()); + exit(1); + } + for (i = 0; i < metrictab_sz; i++) { + metrictab[i].m_user = &metricdesc[i]; + metrictab[i].m_desc = metricdesc[i].md_desc; + ip = (__pmID_int *)&metricdesc[i].md_desc.pmid; + ip->domain = domain; + if (metricdesc[i].md_desc.indom != PM_INDOM_NULL) { + serial = metricdesc[i].md_desc.indom; + iip = (__pmInDom_int *)&metricdesc[i].md_desc.indom; + iip->serial = serial; + iip->pad = 0; + iip->domain = domain; + } + } + + /* + * initialize each of the methods + */ + for (i = 0; i < methodtab_sz; i++) { + methodtab[i].m_init(1); + } +} diff --git a/src/pmdas/aix/disk.c b/src/pmdas/aix/disk.c new file mode 100644 index 0000000..5ab52d6 --- /dev/null +++ b/src/pmdas/aix/disk.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" + +static int ndisk; +static int ndisk_alloc; +static int *fetched; +static perfstat_disk_t *diskstat; + +void +disk_init(int first) +{ + perfstat_id_t id; + int i; + + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; + + ndisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0); + if ((fetched = (int *)malloc(ndisk * sizeof(int))) == NULL) { + fprintf(stderr, "disk_init: fetched malloc[%d] failed: %s\n", + ndisk * sizeof(int), osstrerror()); + exit(1); + } + if ((diskstat = (perfstat_disk_t *)malloc(ndisk * sizeof(perfstat_disk_t))) == NULL) { + fprintf(stderr, "disk_init: diskstat malloc[%d] failed: %s\n", + ndisk * sizeof(perfstat_disk_t), osstrerror()); + exit(1); + } + ndisk_alloc = ndisk; + + /* + * set up instance domain + */ + strcpy(id.name, ""); + ndisk = perfstat_disk(&id, diskstat, sizeof(perfstat_disk_t), ndisk_alloc); + + indomtab[DISK_INDOM].it_numinst = ndisk; + indomtab[DISK_INDOM].it_set = (pmdaInstid *)malloc(ndisk * sizeof(pmdaInstid)); + if (indomtab[DISK_INDOM].it_set == NULL) { + fprintf(stderr, "disk_init: indomtab malloc[%d] failed: %s\n", + ndisk * sizeof(pmdaInstid), osstrerror()); + exit(1); + } + for (i = 0; i < ndisk; i++) { + indomtab[DISK_INDOM].it_set[i].i_inst = i; + indomtab[DISK_INDOM].it_set[i].i_name = strdup(diskstat[i].name); + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_init: ndisk=%d\n", ndisk); + } +#endif +} + +void +disk_prefetch(void) +{ + int i; + + for (i = 0; i < ndisk_alloc; i++) + fetched[i] = 0; +} + +static __uint64_t +disk_derived(pmdaMetric *mdesc, int inst) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + + switch (pmid) { + case PMDA_PMID(0,55): /* disk.dev.total_bytes */ + val = diskstat[inst].rblks + diskstat[inst].wblks; + break; + + default: + fprintf(stderr, "disk_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, val); + } +#endif + + return val; +} + +int +disk_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int offset; + + if (fetched[inst] == 0) { + int sts; + int i; + perfstat_id_t id; + + strcpy(id.name, ""); + sts = perfstat_disk(&id, diskstat, sizeof(perfstat_disk_t), ndisk_alloc); + + /* TODO ... + * - if sts != ndisk, the number of disks has changed, need to set + * fetched[i] to -1 for the missing ones + * - is sts > ndisk possible? worse, if the number of disks is > + * ndisk_alloc what should we do? + * - possibly reshape the instance domain? + * - error handling? + */ + + for (i = 0; i < ndisk; i++) { + fetched[i] = 1; + } + } + + if (fetched[inst] != 1) + return 0; + + offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + if (offset == OFF_NOVALUES) + return 0; + + if (mdesc->m_desc.type == PM_TYPE_U64) { + if (offset == OFF_DERIVED) + atom->ull = disk_derived(mdesc, inst); + else { + __uint64_t *ullp; + ullp = (__uint64_t *)&((char *)&diskstat[inst])[offset]; + atom->ull = *ullp; + } + if (mdesc->m_desc.units.scaleTime == PM_TIME_MSEC) { + /* assumed to be CPU time */ + atom->ull *= 1000 / HZ; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ull); + } +#endif + } + else { + if (offset == OFF_DERIVED) + atom->ul = (__uint32_t)disk_derived(mdesc, inst); + else { + __uint32_t *ulp; + ulp = (__uint32_t *)&((char *)&diskstat[inst])[offset]; + atom->ul = *ulp; + } + if (mdesc->m_desc.units.scaleTime == PM_TIME_MSEC) { + /* assumed to be CPU time */ + atom->ul *= 1000 / HZ; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_fetch: pmid %s inst %d val %lu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ul); + } +#endif + } + + return 1; +} diff --git a/src/pmdas/aix/disk_total.c b/src/pmdas/aix/disk_total.c new file mode 100644 index 0000000..1087d74 --- /dev/null +++ b/src/pmdas/aix/disk_total.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" + +static perfstat_disk_total_t diskstat; +static int fetched; + +void +disk_total_init(int first) +{ + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; +} + +void +disk_total_prefetch(void) +{ + int i; + + fetched = 0; +} + +static __uint64_t +disk_total_derived(pmdaMetric *mdesc, int inst) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + + switch (pmid) { + case PMDA_PMID(0,49): /* disk.all.total_bytes */ + val = diskstat.rblks + diskstat.wblks; + break; + + default: + fprintf(stderr, "disk_total_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_total_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, val); + } +#endif + + return val; +} + +int +disk_total_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int offset; + + if (fetched == 0) { + int sts; + sts = perfstat_disk_total(NULL, &diskstat, sizeof(perfstat_disk_total_t), 1); + if (sts != 1) { + /* TODO - how to find/decode errors? */ + fprintf(stderr, "perfstat_disk_total: failed %s\n", osstrerror()); + fetched = -1; + } + else + fetched = 1; + } + + if (fetched != 1) + return 0; + + offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + if (offset == OFF_NOVALUES) + return 0; + + if (mdesc->m_desc.type == PM_TYPE_U64) { + if (offset == OFF_DERIVED) + atom->ull = disk_total_derived(mdesc, inst); + else { + __uint64_t *ullp; + ullp = (__uint64_t *)&((char *)&diskstat)[offset]; + atom->ull = *ullp; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_total_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ull); + } +#endif + } + else { + if (offset == OFF_DERIVED) + atom->ul = (__uint32_t)disk_total_derived(mdesc, inst); + else { + __uint32_t *ulp; + ulp = (__uint32_t *)&((char *)&diskstat)[offset]; + atom->ul = *ulp; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "disk_total_fetch: pmid %s inst %d val %lu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ul); + } +#endif + } + + return 1; +} diff --git a/src/pmdas/aix/help b/src/pmdas/aix/help new file mode 100644 index 0000000..afce5d8 --- /dev/null +++ b/src/pmdas/aix/help @@ -0,0 +1,61 @@ +# +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# AIX PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ kernel.all.cpu.idle TODO +@ kernel.all.cpu.user TODO +@ kernel.all.cpu.sys TODO +@ kernel.all.cpu.wait.total TODO +@ kernel.percpu.cpu.user TODO +@ kernel.percpu.cpu.idle TODO +@ kernel.percpu.cpu.sys TODO +@ kernel.percpu.cpu.wait.total TODO +@ disk.all.read TODO +@ disk.all.write TODO +@ disk.all.total TODO +@ disk.all.read_bytes TODO +@ disk.all.write_bytes TODO +@ disk.all.total_bytes TODO +@ disk.dev.read TODO +@ disk.dev.write TODO +@ disk.dev.total TODO +@ disk.dev.read_bytes TODO +@ disk.dev.write_bytes TODO +@ disk.dev.total_bytes TODO +@ network.interface.in.packets TODO +@ network.interface.in.bytes TODO +@ network.interface.in.errors TODO +@ network.interface.out.packets TODO +@ network.interface.out.bytes TODO +@ network.interface.out.errors TODO +@ network.interface.total.packets TODO +@ network.interface.total.bytes TODO diff --git a/src/pmdas/aix/netif.c b/src/pmdas/aix/netif.c new file mode 100644 index 0000000..01e0ac6 --- /dev/null +++ b/src/pmdas/aix/netif.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" + +static int nnetif; +static int nnetif_alloc; +static int *fetched; +static perfstat_netinterface_t *netifstat; + +void +netif_init(int first) +{ + perfstat_id_t id; + int i; + + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; + + nnetif = perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t), 0); + if ((fetched = (int *)malloc(nnetif * sizeof(int))) == NULL) { + fprintf(stderr, "netif_init: fetched malloc[%d] failed: %s\n", + nnetif * sizeof(int), osstrerror()); + exit(1); + } + if ((netifstat = (perfstat_netinterface_t *)malloc(nnetif * sizeof(perfstat_netinterface_t))) == NULL) { + fprintf(stderr, "netif_init: netifstat malloc[%d] failed: %s\n", + nnetif * sizeof(perfstat_netinterface_t), osstrerror()); + exit(1); + } + nnetif_alloc = nnetif; + + /* + * set up instance domain + */ + strcpy(id.name, ""); + nnetif = perfstat_netinterface(&id, netifstat, sizeof(perfstat_netinterface_t), nnetif_alloc); + + indomtab[NETIF_INDOM].it_numinst = nnetif; + indomtab[NETIF_INDOM].it_set = (pmdaInstid *)malloc(nnetif * sizeof(pmdaInstid)); + if (indomtab[NETIF_INDOM].it_set == NULL) { + fprintf(stderr, "netif_init: indomtab malloc[%d] failed: %s\n", + nnetif * sizeof(pmdaInstid), osstrerror()); + exit(1); + } + for (i = 0; i < nnetif; i++) { + indomtab[NETIF_INDOM].it_set[i].i_inst = i; + indomtab[NETIF_INDOM].it_set[i].i_name = strdup(netifstat[i].name); + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "netif_init: nnetif=%d\n", nnetif); + } +#endif +} + +void +netif_prefetch(void) +{ + int i; + + for (i = 0; i < nnetif_alloc; i++) + fetched[i] = 0; +} + +static __uint64_t +netif_derived(pmdaMetric *mdesc, int inst) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + + switch (pmid) { + case PMDA_PMID(0,58): /* hinv.nnetif */ + val = nnetif; + break; + + case PMDA_PMID(0,65): /* network.interface.total.packets */ + val = netifstat[inst].ipackets + netifstat[inst].opackets; + break; + + case PMDA_PMID(0,66): /* network.interface.total.bytes */ + val = netifstat[inst].ibytes + netifstat[inst].obytes; + break; + + default: + fprintf(stderr, "netif_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "netif_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, val); + } +#endif + + return val; +} + +int +netif_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int offset; + + if (fetched[inst] == 0) { + int sts; + int i; + perfstat_id_t id; + + strcpy(id.name, ""); + sts = perfstat_netinterface(&id, netifstat, sizeof(perfstat_netinterface_t), nnetif_alloc); + + /* TODO ... + * - if sts != nnetif, the number of network interfaces has changed, + * need to set fetched[i] to -1 for the missing ones + * - is sts > nnetif possible? worse, if the number of network + * interfaces is > nnetif_alloc what should we do? + * - possibly reshape the instance domain? + * - error handling? + */ + + for (i = 0; i < nnetif; i++) { + fetched[i] = 1; + } + } + + /* hinv.nnetif is a singular metric ... so no "instance" for this one */ + if (inst != PM_IN_NULL && fetched[inst] != 1) + return 0; + + offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + if (offset == OFF_NOVALUES) + return 0; + + if (mdesc->m_desc.type == PM_TYPE_U64) { + if (offset == OFF_DERIVED) + atom->ull = netif_derived(mdesc, inst); + else { + __uint64_t *ullp; + ullp = (__uint64_t *)&((char *)&netifstat[inst])[offset]; + atom->ull = *ullp; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "netif_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ull); + } +#endif + } + else { + if (offset == OFF_DERIVED) + atom->ul = (__uint32_t)netif_derived(mdesc, inst); + else { + __uint32_t *ulp; + ulp = (__uint32_t *)&((char *)&netifstat[inst])[offset]; + atom->ul = *ulp; + } +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "netif_fetch: pmid %s inst %d val %lu\n", + pmIDStr(mdesc->m_desc.pmid), inst, atom->ul); + } +#endif + } + + return 1; +} diff --git a/src/pmdas/aix/pmns.disk b/src/pmdas/aix/pmns.disk new file mode 100644 index 0000000..d9ed093 --- /dev/null +++ b/src/pmdas/aix/pmns.disk @@ -0,0 +1,22 @@ +disk { + all + dev +} + +disk.all { + read AIX:0:44 + write AIX:0:45 + total AIX:0:46 + read_bytes AIX:0:47 + write_bytes AIX:0:48 + total_bytes AIX:0:49 +} + +disk.dev { + read AIX:0:50 + write AIX:0:51 + total AIX:0:52 + read_bytes AIX:0:53 + write_bytes AIX:0:54 + total_bytes AIX:0:55 +} diff --git a/src/pmdas/aix/pmns.hinv b/src/pmdas/aix/pmns.hinv new file mode 100644 index 0000000..ba148e2 --- /dev/null +++ b/src/pmdas/aix/pmns.hinv @@ -0,0 +1,6 @@ +hinv { + ncpu AIX:0:56 + ncpu_cfg AIX:0:38 + ndisk AIX:0:57 + nnetif AIX:0:58 +} diff --git a/src/pmdas/aix/pmns.kernel b/src/pmdas/aix/pmns.kernel new file mode 100644 index 0000000..fcafd54 --- /dev/null +++ b/src/pmdas/aix/pmns.kernel @@ -0,0 +1,77 @@ +kernel { + all + percpu +} + +kernel.all { + cpu + io + trap AIX:0:32 + pswitch AIX:0:23 + syscall AIX:0:22 + sysexec AIX:0:33 + sysfork AIX:0:34 + sysread AIX:0:36 + syswrite AIX:0:37 + readch AIX:0:8 + writech AIX:0:9 +} + +kernel.all.cpu { + idle AIX:0:0 + user AIX:0:1 + sys AIX:0:2 + intr AIX:0:31 + wait +} + +kernel.all.cpu.wait { + total AIX:0:3 +} + +kernel.all.io { + bread AIX:0:14 + bwrite AIX:0:15 + lread AIX:0:16 + lwrite AIX:0:17 + phread AIX:0:26 + phwrite AIX:0:27 + devintrs AIX:0:28 + softintrs AIX:0:10 + namei AIX:0:35 +} + +kernel.percpu { + cpu + io + pswitch AIX:0:25 + syscall AIX:0:24 + sysexec AIX:0:39 + sysfork AIX:0:40 + sysread AIX:0:42 + syswrite AIX:0:43 + readch AIX:0:11 + writech AIX:0:12 +} + +kernel.percpu.cpu { + idle AIX:0:4 + user AIX:0:5 + sys AIX:0:6 + intr AIX:0:13 + wait +} + +kernel.percpu.cpu.wait { + total AIX:0:7 +} + +kernel.percpu.io { + bread AIX:0:18 + bwrite AIX:0:19 + lread AIX:0:20 + lwrite AIX:0:21 + phread AIX:0:29 + phwrite AIX:0:30 + namei AIX:0:41 +} diff --git a/src/pmdas/aix/pmns.mem b/src/pmdas/aix/pmns.mem new file mode 100644 index 0000000..bfe42d2 --- /dev/null +++ b/src/pmdas/aix/pmns.mem @@ -0,0 +1,2 @@ +mem { +} diff --git a/src/pmdas/aix/pmns.network b/src/pmdas/aix/pmns.network new file mode 100644 index 0000000..a469296 --- /dev/null +++ b/src/pmdas/aix/pmns.network @@ -0,0 +1,26 @@ +network { + interface +} + +network.interface { + in + out + total +} + +network.interface.in { + packets AIX:0:59 + bytes AIX:0:60 + errors AIX:0:61 +} + +network.interface.out { + packets AIX:0:62 + bytes AIX:0:63 + errors AIX:0:64 +} + +network.interface.total { + packets AIX:0:65 + bytes AIX:0:66 +} diff --git a/src/pmdas/aix/root b/src/pmdas/aix/root new file mode 100644 index 0000000..ec99587 --- /dev/null +++ b/src/pmdas/aix/root @@ -0,0 +1,20 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { + kernel + disk + mem + network + hinv +} + +#include "pmns.kernel" +#include "pmns.disk" +#include "pmns.mem" +#include "pmns.network" +#include "pmns.hinv" + diff --git a/src/pmdas/apache/Apache.pmchart b/src/pmdas/apache/Apache.pmchart new file mode 100644 index 0000000..b830e4e --- /dev/null +++ b/src/pmdas/apache/Apache.pmchart @@ -0,0 +1,9 @@ +#pmchart +Version 2.0 host dynamic + +Chart Title "Apache Hit Rate" Style plot + Plot Color #-cycle Host * Metric apache.requests_per_sec +Chart Title "Apache Data Rate" Style plot + Plot Color #-cycle Host * Metric apache.bytes_per_sec +# +# Created Fri Jun 10 05:16:55 2005 diff --git a/src/pmdas/apache/GNUmakefile b/src/pmdas/apache/GNUmakefile new file mode 100644 index 0000000..8465706 --- /dev/null +++ b/src/pmdas/apache/GNUmakefile @@ -0,0 +1,61 @@ +# +# Copyright (C) 2000 Michal Kara. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = apache +DOMAIN = APACHE +TARGETS = $(IAM)$(EXECSUFFIX) +CFILES = apache.c +SCRIPTS = Install Remove +DFILES = README +LSRCFILES= $(SCRIPTS) pmns help root Apache.pmchart $(DFILES) \ + pmlogconf.summary pmlogconf.processes pmlogconf.uptime + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMCHART = $(PCP_VAR_DIR)/config/pmchart + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so $(TARGETS) +LCFLAGS = -I$(TOPDIR)/src/libpcp_http/src +LLDFLAGS= -L$(TOPDIR)/src/libpcp_http/src +LLDLIBS = -lpcp_http $(PCP_PMDALIB) + +default: build-me + +include $(BUILDRULES) + +build-me: $(TARGETS) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) pmns help root domain.h $(PMDADIR) + $(INSTALL) -m 644 Apache.pmchart $(PMCHART)/Apache + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary + $(INSTALL) -m 644 pmlogconf.processes $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/processes + $(INSTALL) -m 644 pmlogconf.uptime $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/uptime + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +apache.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp : default + +install_pcp : install diff --git a/src/pmdas/apache/Install b/src/pmdas/apache/Install new file mode 100755 index 0000000..8bd8e72 --- /dev/null +++ b/src/pmdas/apache/Install @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Copyright (c) 2010 Aconex. All Rights Reserved. +# Copyright (c) 1999,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the apache PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=apache +pmda_interface=3 +forced_restart=false + +# Override interactive dialog from pmdaSetup in pmdaproc.sh +__choose_mode() +{ + echo "Installing the \"$iam\" Performance Metrics Domain Agent (PMDA) ..." + echo +} + +pmdaSetup + +if $do_pmda +then + args="" + + $PCP_ECHO_PROG $PCP_ECHO_N "Apache port number [80]? ""$PCP_ECHO_C" + read value + + if [ "X$value" = "X" ] + then + : + elif [ "X`expr 0 + $value 2>/dev/null`" != "X$value" ] + then + echo "-- Sorry, port number must be numeric (not $value), ignored --" >&2 + else + args="$args -P $value" + fi +fi + +pmdaInstall +exit 0 diff --git a/src/pmdas/apache/README b/src/pmdas/apache/README new file mode 100644 index 0000000..75eb7e3 --- /dev/null +++ b/src/pmdas/apache/README @@ -0,0 +1,84 @@ +Apache PMDA +=========== + +Export information from apache server info. + +Author: Michal Kara + +To use this PMDA, you must be running Apache 1.3.2 or later (for the +extended statistics). + +First configure your apache http server to enable status support. +The full procedure is documented at +http://www.apache.org/docs/mod/mod_status.html + +Edit /etc/apache2/mods-enabled/status.conf and enable the following: + + + SetHandler server-status + Allow from all + +ExtendedStatus On + +Note: you may wish to restrict access to the server status reports to +the local host only. See the Apache web site for instructions. + +You will need to stop and restart your Apache server after making +this change: +# apachectl restart + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT apache + +The metrics give basic information about Apache performance and about usage of +server threads. + +Installation +============ + + + # cd $PCP_PMDAS_DIR/apache + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and optionally specify the port number where Apache is running. + Everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/apache + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/apache.log) should be checked for any warnings + or errors. + +Credits +======= + +Original PMDA was written by Michal Kara. + +This PMDA embeds a (slightly modified) lightweight "Http Fetcher" library, +written by Lyle Hanson (lhanson@users.sourceforge.net) (C) 2001, 2003, 2004 +http://http-fetcher.sourceforge.net diff --git a/src/pmdas/apache/Remove b/src/pmdas/apache/Remove new file mode 100644 index 0000000..8d9fae1 --- /dev/null +++ b/src/pmdas/apache/Remove @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 1999,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the apache PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=apache +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/apache/apache.c b/src/pmdas/apache/apache.c new file mode 100644 index 0000000..3597308 --- /dev/null +++ b/src/pmdas/apache/apache.c @@ -0,0 +1,538 @@ +/* + * Apache PMDA + * + * Copyright (C) 2012-2014 Red Hat. + * Copyright (C) 2008-2010 Aconex. All Rights Reserved. + * Copyright (C) 2000 Michal Kara. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "http_fetcher.h" +#include + +static char url[256]; +static char uptime_s[64]; +static char *username; + +static int http_port = 80; +static char *http_server = "localhost"; +static char *http_path = "server-status"; + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + { "server", 1, 'S', "HOST", "use remote host, instead of localhost" }, + { "port", 1, 'P', "PORT", "use given port on server, instead of port 80" }, + { "location", 1, 'L', "LOC", "use location on server, instead of 'server-status'" }, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_TEXT("\nExactly one of the following options may appear:"), + PMDAOPT_INET, + PMDAOPT_PIPE, + PMDAOPT_UNIX, + PMDAOPT_IPV6, + PMDA_OPTIONS_END +}; + +static pmdaOptions opts = { + .short_options = "D:d:i:l:pu:L:P:S:U:6:?", + .long_options = longopts, +}; + +static pmdaMetric metrictab[] = { +/* apache.total_accesses */ + { NULL, { PMDA_PMID(0,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.total_kbytes */ + { NULL, { PMDA_PMID(0,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, +/* apache.uptime */ + { NULL, { PMDA_PMID(0,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) } }, +/* apache.requests_per_sec */ + { NULL, { PMDA_PMID(0,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE) } }, +/* apache.bytes_per_sec */ + { NULL, { PMDA_PMID(0,4), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) } }, +/* apache.bytes_per_request */ + { NULL, { PMDA_PMID(0,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,-1,PM_SPACE_BYTE,0,PM_COUNT_ONE) } }, +/* apache.busy_servers */ + { NULL, { PMDA_PMID(0,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.idle_servers */ + { NULL, { PMDA_PMID(0,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_waiting */ + { NULL, { PMDA_PMID(0,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_starting */ + { NULL, { PMDA_PMID(0,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_reading */ + { NULL, { PMDA_PMID(0,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_writing_reply */ + { NULL, { PMDA_PMID(0,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_keepalive */ + { NULL, { PMDA_PMID(0,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_dns_lookup */ + { NULL, { PMDA_PMID(0,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_logging */ + { NULL, { PMDA_PMID(0,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_finishing */ + { NULL, { PMDA_PMID(0,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_open_slot */ + { NULL, { PMDA_PMID(0,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_closing */ + { NULL, { PMDA_PMID(0,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.sb_idle_cleanup */ + { NULL, { PMDA_PMID(0,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* apache.uptime_s */ + { NULL, { PMDA_PMID(0,19), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, +}; + +/* + * To speed everything up, the PMDA is caching the data. + * Values are refreshed only if older than one second. + */ +struct { + unsigned int flags; /* Tells which values are valid */ + unsigned int timeout; /* There was a timeout (a bool) */ + time_t timestamp; /* Time of last attempted fetch */ + + __uint64_t uptime; + __uint64_t total_accesses; + __uint64_t total_kbytes; + double requests_per_sec; + double bytes_per_sec; + unsigned int bytes_per_request; + unsigned int busy_servers; + unsigned int idle_servers; + unsigned int sb_waiting; + unsigned int sb_starting; + unsigned int sb_reading; + unsigned int sb_writing_reply; + unsigned int sb_keepalive; + unsigned int sb_dns_lookup; + unsigned int sb_logging; + unsigned int sb_finishing; + unsigned int sb_open_slot; + unsigned int sb_closing; + unsigned int sb_idle_cleanup; +} data; + +/* + * Valid values of flags - tell us which values are currently setup. + * Depending on server version and configuration, some may be missing. + */ +enum { + ACCESSES = (1<<0), + KILOBYTES = (1<<1), + UPTIME = (1<<2), + REQPERSEC = (1<<3), + BYTESPERSEC = (1<<4), + BYTESPERREQ = (1<<5), + BUSYSERVERS = (1<<6), + IDLESERVERS = (1<<7), + SCOREBOARD = (1<<8), +}; + +static void uptime_string(time_t now, char *s, size_t sz) +{ + int days, hours, minutes, seconds; + + days = now / (60 * 60 * 24); + now %= (60 * 60 * 24); + hours = now / (60 * 60); + now %= (60 * 60); + minutes = now / 60; + now %= 60; + seconds = now; + + if (days > 1) + snprintf(s, sz, "%ddays %02d:%02d:%02d", days, hours, minutes, seconds); + else if (days == 1) + snprintf(s, sz, "%dday %02d:%02d:%02d", days, hours, minutes, seconds); + else + snprintf(s, sz, "%02d:%02d:%02d", hours, minutes, seconds); +} + +static void dumpData(void) +{ + uptime_string(data.uptime, uptime_s, sizeof(uptime_s)); + fprintf(stderr, "Apache data from %s port %d, path %s:\n", + http_server, http_port, http_path); + fprintf(stderr, " flags=0x%x timeout=%d timestamp=%lu\n", + data.flags, data.timeout, (unsigned long)data.timestamp); + fprintf(stderr, " uptime=%" PRIu64 " (%s)\n", data.uptime, uptime_s); + fprintf(stderr, " accesses=%" PRIu64 " kbytes=%" PRIu64 + " req/sec=%.2f b/sec=%.2f\n", + data.total_accesses, data.total_kbytes, + data.requests_per_sec, data.bytes_per_sec); + fprintf(stderr, " b/req=%u busyserv=%u idleserv=%u\n", + data.bytes_per_request, data.busy_servers, data.idle_servers); + fprintf(stderr, " scoreboard: waiting=%u starting=%u reading=%u\n", + data.sb_waiting, data.sb_starting, data.sb_reading); + fprintf(stderr, " writing_reply=%u keepalive=%u dn_lookup=%u\n", + data.sb_writing_reply, data.sb_keepalive, data.sb_dns_lookup); + fprintf(stderr, " logging=%u finishing=%u open_slot=%u\n", + data.sb_logging, data.sb_finishing, data.sb_open_slot); + fprintf(stderr, " closing=%u idle_cleanup=%u\n", + data.sb_closing, data.sb_idle_cleanup); +} + +/* + * Refresh data. Returns 1 of OK, 0 on error. + */ +static int refreshData(time_t now) +{ + char *res = NULL; + int len; + char *s,*s2,*s3; + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Doing http_fetch(%s)\n", url); + + len = http_fetch(url, &res); + if (len < 0) { + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_ERR, "HTTP fetch (stats) failed: %s\n", http_strerror()); + data.timeout = http_getTimeoutError(); + if (data.timeout) + data.timestamp = now; /* Don't retry too soon */ + if (res) + free(res); + return 0; /* failed */ + } + + memset(&data, 0, sizeof(data)); + + for (s = res; *s; ) { + s2 = s; + s3 = NULL; + for (; *s && *s != 10; s++) { + if (*s == ':') { + s3 = s + 1; + if (*s3) { + *s3++ = 0; + s++; + } + } + } + + if (*s == 10) + *s++ = 0; + + if (strcmp(s2, "CPULoad:") == 0) + /* ignored */ ; + else if (strcmp(s2, "Total Accesses:") == 0) { + data.total_accesses = strtoull(s3, (char **)NULL, 10); + data.flags |= ACCESSES; + } + else if (strcmp(s2, "Total kBytes:") == 0) { + data.total_kbytes = strtoull(s3, (char **)NULL, 10); + data.flags |= KILOBYTES; + } + else if (strcmp(s2, "Uptime:") == 0) { + data.uptime = strtoull(s3, (char **)NULL, 10); + data.flags |= UPTIME; + } + else if (strcmp(s2, "ReqPerSec:") == 0) { + data.requests_per_sec = strtod(s3, (char **)NULL); + data.flags |= REQPERSEC; + } + else if (strcmp(s2, "BytesPerSec:") == 0) { + data.bytes_per_sec = strtod(s3, (char **)NULL); + data.flags |= BYTESPERSEC; + } + else if (strcmp(s2, "BytesPerReq:") == 0) { + data.bytes_per_request = (unsigned int)strtoul(s3, (char **)NULL, 10); + data.flags |= BYTESPERREQ; + } + else if ((strcmp(s2, "BusyServers:") == 0) || + (strcmp(s2, "BusyWorkers:") == 0)) { + data.busy_servers = (unsigned int)strtoul(s3, (char **)NULL, 10); + data.flags |= BUSYSERVERS; + } + else if ((strcmp(s2, "IdleServers:") == 0) || + (strcmp(s2, "IdleWorkers:") == 0)) { + data.idle_servers = (unsigned int)strtoul(s3, (char **)NULL, 10); + data.flags |= IDLESERVERS; + } + else if (strcmp(s2, "Scoreboard:") == 0) { + data.flags |= SCOREBOARD; + while(*s3) { + switch(*s3) { + case '_': + data.sb_waiting++; + break; + case 'S': + data.sb_starting++; + break; + case 'R': + data.sb_reading++; + break; + case 'W': + data.sb_writing_reply++; + break; + case 'K': + data.sb_keepalive++; + break; + case 'D': + data.sb_dns_lookup++; + break; + case 'C': + data.sb_closing++; + break; + case 'L': + data.sb_logging++; + break; + case 'G': + data.sb_finishing++; + break; + case 'I': + data.sb_idle_cleanup++; + break; + case '.': + data.sb_open_slot++; + break; + default: + if (pmDebug & DBG_TRACE_APPL1) { + __pmNotifyErr(LOG_WARNING, + "Unknown scoreboard character '%c'\n", *s3); + } + } + s3++; + } + } + else if (pmDebug & DBG_TRACE_APPL1) { + __pmNotifyErr(LOG_WARNING, "Unknown value name '%s'!\n", s2); + } + } + + data.timestamp = now; + + if (pmDebug & DBG_TRACE_APPL2) + dumpData(); + free(res); + return 1; +} + +static int +apache_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + time_t now = time(NULL); + + if (now > data.timestamp && !refreshData(now + 1)) + return PM_ERR_AGAIN; + if (data.timeout) + return PM_ERR_AGAIN; + + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +apache_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (idp->cluster != 0) + return PM_ERR_PMID; + else if (inst != PM_IN_NULL) + return PM_ERR_INST; + + switch (idp->item) { + case 0: + if (!(data.flags & ACCESSES)) + return 0; + atom->ull = data.total_accesses; + break; + case 1: + if (!(data.flags & KILOBYTES)) + return 0; + atom->ull = data.total_kbytes; + break; + case 2: + if (!(data.flags & UPTIME)) + return 0; + atom->ull = data.uptime; + break; + case 3: + if (!(data.flags & REQPERSEC)) + return 0; + atom->d = data.requests_per_sec; + break; + case 4: + if (!(data.flags & BYTESPERSEC)) + return 0; + atom->d = data.bytes_per_sec; + break; + case 5: + if (!(data.flags & BYTESPERREQ)) + return 0; + atom->ul = data.bytes_per_request; + break; + case 6: + if (!(data.flags & BUSYSERVERS)) + return 0; + atom->ul = data.busy_servers; + break; + case 7: + if (!(data.flags & IDLESERVERS)) + return 0; + atom->ul = data.idle_servers; + break; + case 8: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_waiting; + break; + case 9: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_starting; + break; + case 10: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_reading; + break; + case 11: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_writing_reply; + break; + case 12: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_keepalive; + break; + case 13: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_dns_lookup; + break; + case 14: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_logging; + break; + case 15: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_finishing; + break; + case 16: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_open_slot; + break; + case 17: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_closing; + break; + case 18: + if (!(data.flags & SCOREBOARD)) + return 0; + atom->ul = data.sb_idle_cleanup; + break; + case 19: + if (!(data.flags & UPTIME)) + return 0; + uptime_string(data.uptime, uptime_s, sizeof(uptime_s)); + atom->cp = uptime_s; + break; + + default: + return PM_ERR_PMID; + } + + return 1; +} + +void +apache_init(pmdaInterface *dp) +{ + __pmSetProcessIdentity(username); + + http_setTimeout(1); + http_setUserAgent(pmProgname); + snprintf(url, sizeof(url), "http://%s:%u/%s?auto", http_server, http_port, http_path); + + dp->version.two.fetch = apache_fetch; + pmdaSetFetchCallBack(dp, apache_fetchCallBack); + pmdaInit(dp, NULL, 0, metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} + +int +main(int argc, char **argv) +{ + int c, sep = __pmPathSeparator(); + pmdaInterface pmda; + char helppath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(helppath, sizeof(helppath), "%s%c" "apache" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&pmda, PMDA_INTERFACE_3, pmProgname, APACHE, "apache.log", + helppath); + + while ((c = pmdaGetOptions(argc, argv, &opts, &pmda)) != EOF) { + switch(c) { + case 'S': + http_server = opts.optarg; + break; + case 'P': + http_port = (int)strtol(opts.optarg, (char **)NULL, 10); + break; + case 'L': + if (opts.optarg[0] == '/') + opts.optarg++; + http_path = opts.optarg; + break; + } + } + + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + if (opts.username) + username = opts.username; + + pmdaOpenLog(&pmda); + apache_init(&pmda); + pmdaConnect(&pmda); + pmdaMain(&pmda); + exit(0); +} diff --git a/src/pmdas/apache/help b/src/pmdas/apache/help new file mode 100644 index 0000000..08ee3b3 --- /dev/null +++ b/src/pmdas/apache/help @@ -0,0 +1,91 @@ +# +# Copyright (C) 2000 Michal Kara. All Rights Reserved. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# apache PMDA help file in the ASCII format +# + +@apache.total_accesses Number of accesses since Apache started +Number of accesses (requests) Apache handled since start of the server. + +@apache.total_kbytes KBytes transferred since Apache started +Number of kilobytes Apache transferred since start of the server. + +@apache.uptime Seconds since Apache started +Number of seconds elapsed since the server was started. + +@apache.requests_per_sec Requests per second +Average requests-per-second rate. Apache computes this as +(total_accesses / uptime), so its better to use total_accesses +to get current request rate. + +@apache.bytes_per_sec Bytes per second +Average bytes-per-second rate. Apache computes this as +(total_kbytes / uptime), so its better to use total_kbytes +to get current throughput. + +@apache.bytes_per_requests Bytes per request +Average number of bytes per request. Apache computes this as +total_kbytes/total_accesses. + +@apache.busy_servers Number of working processes +Number of Apache processes which are now working - reading requests, +receiving data, etc. Together with idle_servers this number gives the +total number of running Apache processes. + +@apache.idle_servers Number of idle processes +Number of Apache processes which are now idle - waiting for connection. +Together with busy_servers this number gives the total number of running +Apache processes. + +@apache.sb_waiting Number of waiting processes +Number of Apache processes waiting for connection. + +@apache.sb_starting Number of starting processes +Number of Apache processes just starting. + +@apache.sb_reading Number of processes reading +Number of Apache processes reading a request. + +@apache.sb_writing_reply Number of processes writing +Number of Apache processes writing a reply. + +@apache.sb_keepalive Number of processes keeping-alive connection +Number of Apache processes waiting for next request on a keep-alive +connection. + +@apache.sb_dns_lookup Number of processes doing DNS lookup +Number of Apache processes currently doing DNS lookup. + +@apache.sb_logging Number of processes writing to log +Number of Apache processes writing to a log. + +@apache.sb_finishing Number of proceses finishing +Number of Apache processes gracefuly finishing (becase the +RequestsPerChild limit was reached). + +@apache.sb_open_slot Number of open slots +Number of open "slots" which could be occupied by a process. Together +with busy_servers and idle_servers, this gives the MaxClients value. + +@apache.sb_closing Number of processes closing client connections +Number of Apache processes closing client connections + +@apache.sb_idle_cleanup Number of processes performing idle cleanup +Number of Apache processes performing idle cleanup + +@apache.uptime_s Time since Apache started, as a string diff --git a/src/pmdas/apache/pmlogconf.processes b/src/pmdas/apache/pmlogconf.processes new file mode 100644 index 0000000..9513e2d --- /dev/null +++ b/src/pmdas/apache/pmlogconf.processes @@ -0,0 +1,14 @@ +#pmlogconf-setup 2.0 +ident Apache process state information +probe apache.sb_waiting + apache.sb_waiting + apache.sb_starting + apache.sb_reading + apache.sb_writing_reply + apache.sb_keepalive + apache.sb_dns_lookup + apache.sb_logging + apache.sb_finishing + apache.sb_open_slot + apache.sb_closing + apache.sb_idle_cleanup diff --git a/src/pmdas/apache/pmlogconf.summary b/src/pmdas/apache/pmlogconf.summary new file mode 100644 index 0000000..7a88d85 --- /dev/null +++ b/src/pmdas/apache/pmlogconf.summary @@ -0,0 +1,10 @@ +#pmlogconf-setup 2.0 +ident Apache summary information +probe apache.total_accesses exists ? include : exclude + apache.total_accesses + apache.total_kbytes + apache.requests_per_sec + apache.bytes_per_sec + apache.bytes_per_requests + apache.busy_servers + apache.idle_servers diff --git a/src/pmdas/apache/pmlogconf.uptime b/src/pmdas/apache/pmlogconf.uptime new file mode 100644 index 0000000..2b2b7b7 --- /dev/null +++ b/src/pmdas/apache/pmlogconf.uptime @@ -0,0 +1,6 @@ +#pmlogconf-setup 2.0 +ident Apache uptime +probe apache.uptime exists ? include : exclude +delta 5 minutes + apache.uptime + apache.uptime_s diff --git a/src/pmdas/apache/pmns b/src/pmdas/apache/pmns new file mode 100644 index 0000000..c4d9f88 --- /dev/null +++ b/src/pmdas/apache/pmns @@ -0,0 +1,43 @@ +/* + * Metrics for apache PMDA + * + * Copyright (C) 2000 Michal Kara. All Rights Reserved. + * Copyright (c) 2008 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +apache { + total_accesses APACHE:0:0 + total_kbytes APACHE:0:1 + uptime APACHE:0:2 + requests_per_sec APACHE:0:3 + bytes_per_sec APACHE:0:4 + bytes_per_requests APACHE:0:5 + busy_servers APACHE:0:6 + idle_servers APACHE:0:7 + sb_waiting APACHE:0:8 + sb_starting APACHE:0:9 + sb_reading APACHE:0:10 + sb_writing_reply APACHE:0:11 + sb_keepalive APACHE:0:12 + sb_dns_lookup APACHE:0:13 + sb_logging APACHE:0:14 + sb_finishing APACHE:0:15 + sb_open_slot APACHE:0:16 + sb_closing APACHE:0:17 + sb_idle_cleanup APACHE:0:18 + uptime_s APACHE:0:19 +} diff --git a/src/pmdas/apache/root b/src/pmdas/apache/root new file mode 100644 index 0000000..7d2b7ed --- /dev/null +++ b/src/pmdas/apache/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { apache } + +#include "pmns" + diff --git a/src/pmdas/bash/GNUmakefile b/src/pmdas/bash/GNUmakefile new file mode 100644 index 0000000..0143cac --- /dev/null +++ b/src/pmdas/bash/GNUmakefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 2012 Nathan Scott. All Rights Reversed. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = bash +DOMAIN = BASH + +TARGETS = $(IAM)$(EXECSUFFIX) +LLDLIBS = $(PCP_PMDALIB) +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LDIRT = domain.h *.log *.dir *.pag so_locations $(TARGETS) + +CFILES = event.c bash.c util.c +HFILES = event.h +SCRIPTS = bashproc.sh pcp.sh +SAMPLES = test-child.sh test-trace.sh +LSRCFILES = Install Remove pmns help root $(SCRIPTS) $(SAMPLES) + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "mingw" +build-me: +install: +else +build-me: $(TARGETS) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 root help pmns $(PMDADIR) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h + $(INSTALL) -m 644 bashproc.sh $(PCP_SHARE_DIR)/lib/bashproc.sh + $(INSTALL) -m 644 pcp.sh $(PCP_ETC_DIR)/pcp.sh +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +bash.o: domain.h +event.o bash.o util.o: event.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp : default + +install_pcp : install diff --git a/src/pmdas/bash/Install b/src/pmdas/bash/Install new file mode 100644 index 0000000..5cb784d --- /dev/null +++ b/src/pmdas/bash/Install @@ -0,0 +1,36 @@ +#! /bin/sh +# +# Copyright (c) 2012 Nathan Scott. All rights reversed. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the bash PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=bash +dso_opt=true +socket_opt=true +socket_inet_def=2082 +forced_restart=false +pmda_interface=5 + +if [ ! -e "$PCP_TMP_DIR/pmdabash" ] +then + echo "creating $PCP_TMP_DIR/pmdabash" + mkdir -p -m 1777 "$PCP_TMP_DIR/pmdabash" +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/bash/README b/src/pmdas/bash/README new file mode 100644 index 0000000..58ca47f --- /dev/null +++ b/src/pmdas/bash/README @@ -0,0 +1,50 @@ +Bash PMDA +========= + +This PMDA exports information from bash (GNU Bourne-Again SHell) +processes which are setup to send trace information to PCP. This +requires functionality present in bash version four or later. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT bash + +Installation +============ + + + # cd $PCP_PMDAS_DIR/bash + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/bash + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/bash.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/bash/Remove b/src/pmdas/bash/Remove new file mode 100644 index 0000000..9f87da3 --- /dev/null +++ b/src/pmdas/bash/Remove @@ -0,0 +1,23 @@ +#! /bin/sh +# +# Copyright (c) 2012 Nathan Scott. All rights reversed. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the bash PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh +iam=bash +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/bash/bash.c b/src/pmdas/bash/bash.c new file mode 100644 index 0000000..e25ef2e --- /dev/null +++ b/src/pmdas/bash/bash.c @@ -0,0 +1,472 @@ +/* + * Bash -x trace PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2012 Nathan Scott. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "domain.h" +#include "event.h" +#include "pmda.h" + +#define DEFAULT_MAXMEM (2 * 1024 * 1024) /* 2 megabytes */ +long bash_maxmem; + +static int bash_interval_expired; +static struct timeval bash_interval = { 1, 0 }; + +static char *username; + +#define BASH_INDOM 0 +static pmdaIndom indoms[] = { { BASH_INDOM, 0, NULL } }; +#define INDOM_COUNT (sizeof(indoms)/sizeof(indoms[0])) + +static pmdaMetric metrics[] = { + +#define bash_xtrace_numclients 0 + { NULL, { PMDA_PMID(0,bash_xtrace_numclients), PM_TYPE_U32, + BASH_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +#define bash_xtrace_maxmem 1 + { NULL, { PMDA_PMID(0,bash_xtrace_maxmem), PM_TYPE_U64, + BASH_INDOM, PM_SEM_DISCRETE, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +#define bash_xtrace_queuemem 2 + { NULL, { PMDA_PMID(0,bash_xtrace_queuemem), PM_TYPE_U64, + BASH_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +#define bash_xtrace_count 3 + { NULL, { PMDA_PMID(0,bash_xtrace_count), PM_TYPE_U32, + BASH_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +#define bash_xtrace_records 4 + { NULL, { PMDA_PMID(0,bash_xtrace_records), PM_TYPE_EVENT, + BASH_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +#define bash_xtrace_parameters_pid 5 + { NULL, { PMDA_PMID(0,bash_xtrace_parameters_pid), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +#define bash_xtrace_parameters_parent 6 + { NULL, { PMDA_PMID(0,bash_xtrace_parameters_parent), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +#define bash_xtrace_parameters_lineno 7 + { NULL, { PMDA_PMID(0,bash_xtrace_parameters_lineno), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +#define bash_xtrace_parameters_function 8 + { NULL, { PMDA_PMID(0,bash_xtrace_parameters_function), PM_TYPE_STRING, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +#define bash_xtrace_parameters_command 9 + { NULL, { PMDA_PMID(0,bash_xtrace_parameters_command), PM_TYPE_STRING, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; +#define METRIC_COUNT (sizeof(metrics)/sizeof(metrics[0])) + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + { "memory", 1, 'm', "SIZE", "maximum memory used per logfile (default 2MB)" }, + { "interval", 1, 's', "DELTA", "default delay between iterations (default 1 sec)" }, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +static pmdaOptions opts = { + .short_options = "D:d:l:m:s:U:?", + .long_options = longopts, +}; + + +static void +check_processes(int context) +{ + pmdaEventNewClient(context); + event_refresh(indoms[BASH_INDOM].it_indom); +} + +static int +bash_instance(pmInDom indom, int inst, char *name, + __pmInResult **result, pmdaExt *pmda) +{ + check_processes(pmda->e_context); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +bash_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + check_processes(pmda->e_context); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +bash_trace_parser(bash_process_t *bash, bash_trace_t *trace, + struct timeval *timestamp, const char *buffer, size_t size) +{ + trace->flags = (PM_EVENT_FLAG_ID | PM_EVENT_FLAG_PARENT); + + /* empty event inserted into queue to signal process has exited */ + if (size <= 0) { + trace->flags |= PM_EVENT_FLAG_END; + if (fstat(bash->fd, &bash->stat) < 0 || !S_ISFIFO(bash->stat.st_mode)) + memcpy(&trace->timestamp, timestamp, sizeof(*timestamp)); + else + process_stat_timestamp(bash, &trace->timestamp); + close(bash->fd); + } else { + char *p = (char *)buffer, *end = (char *)buffer + size - 1; + int sz, time = -1; + + /* version 1 format: time, line#, function, and command line */ + p += extract_int(p, "time:", sizeof("time:")-1, &time); + p += extract_int(p, "line:", sizeof("line:")-1, &trace->line); + p += extract_str(p, end - p, "func:", sizeof("func:")-1, + trace->function, sizeof(trace->function)); + sz = extract_cmd(p, end - p, "+", sizeof("+")-1, + trace->command, sizeof(trace->command)); + if (sz <= 0) /* wierd trace - no command */ + trace->command[0] = '\0'; + + if (strncmp(trace->command, "pcp_trace ", 10) == 0 || + strncmp(trace->function, "pcp_trace", 10) == 0) + return 1; /* suppress tracing function, its white noise */ + + if (time != -1) { /* normal case */ + trace->timestamp.tv_sec = bash->starttime.tv_sec + time; + trace->timestamp.tv_usec = bash->starttime.tv_usec; + } else { /* wierd trace */ + memcpy(&trace->timestamp, timestamp, sizeof(*timestamp)); + } + + if (event_start(bash, &trace->timestamp)) + trace->flags |= PM_EVENT_FLAG_START; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, + "event parsed: flags: %x time: %d line: %d func: '%s' cmd: '%s'", + trace->flags, time, trace->line, trace->function, trace->command); + } + return 0; +} + +static int +bash_trace_decoder(int eventarray, + void *buffer, size_t size, struct timeval *timestamp, void *data) +{ + pmAtomValue atom; + bash_process_t *process = (bash_process_t *)data; + bash_trace_t trace = { 0 }; + pmID pmid; + int sts, count = 0; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "bash_trace_decoder[%ld bytes]", (long)size); + + if (bash_trace_parser(process, &trace, timestamp, (const char *)buffer, size)) + return 0; + + sts = pmdaEventAddRecord(eventarray, &trace.timestamp, trace.flags); + if (sts < 0) + return sts; + + atom.ul = process->pid; + pmid = metrics[bash_xtrace_parameters_pid].m_desc.pmid; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_U32, &atom); + if (sts < 0) + return sts; + count++; + + atom.ul = process->parent; + pmid = metrics[bash_xtrace_parameters_parent].m_desc.pmid; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_U32, &atom); + if (sts < 0) + return sts; + count++; + + if (trace.line) { + atom.ul = trace.line; + pmid = metrics[bash_xtrace_parameters_lineno].m_desc.pmid; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_U32, &atom); + if (sts < 0) + return sts; + count++; + } + + if (trace.function[0] != '\0') { + atom.cp = trace.function; + pmid = metrics[bash_xtrace_parameters_function].m_desc.pmid; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_STRING, &atom); + if (sts < 0) + return sts; + count++; + } + + if (trace.command[0] != '\0') { + atom.cp = trace.command; + pmid = metrics[bash_xtrace_parameters_command].m_desc.pmid; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_STRING, &atom); + if (sts < 0) + return sts; + count++; + } + + return count; +} + +static int +bash_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + bash_process_t *bp; + + if (idp->cluster != 0) + return PM_ERR_PMID; + + switch (idp->item) { + case bash_xtrace_maxmem: + atom->ull = (unsigned long long)bash_maxmem; + return PMDA_FETCH_STATIC; + case bash_xtrace_parameters_pid: + case bash_xtrace_parameters_parent: + case bash_xtrace_parameters_lineno: + case bash_xtrace_parameters_function: + case bash_xtrace_parameters_command: + return PMDA_FETCH_NOVALUES; + } + + if (PMDA_CACHE_ACTIVE != + pmdaCacheLookup(indoms[BASH_INDOM].it_indom, inst, NULL, (void **)&bp)) + return PM_ERR_INST; + + switch (idp->item) { + case bash_xtrace_numclients: + return pmdaEventQueueClients(bp->queueid, atom); + case bash_xtrace_queuemem: + return pmdaEventQueueMemory(bp->queueid, atom); + case bash_xtrace_count: + return pmdaEventQueueCounter(bp->queueid, atom); + case bash_xtrace_records: + return pmdaEventQueueRecords(bp->queueid, atom, pmdaGetContext(), + bash_trace_decoder, bp); + } + + return PM_ERR_PMID; +} + +static int +bash_store_metric(pmValueSet *vsp, int context) +{ + __pmID_int *idp = (__pmID_int *)&vsp->pmid; + pmInDom processes = indoms[BASH_INDOM].it_indom; + int sts; + + if (idp->cluster != 0 || idp->item != bash_xtrace_records) + return PM_ERR_PERMISSION; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "bash_store_metric walking bash set"); + + pmdaCacheOp(processes, PMDA_CACHE_WALK_REWIND); + while ((sts = pmdaCacheOp(processes, PMDA_CACHE_WALK_NEXT)) != -1) { + bash_process_t *bp; + + if (!pmdaCacheLookup(processes, sts, NULL, (void **)&bp)) + continue; + if ((sts = pmdaEventSetAccess(context, bp->queueid, 1)) < 0) + return sts; + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, + "Access granted client=%d bash=%d queueid=%d", + context, bp->pid, bp->queueid); + } + return 0; +} + +static int +bash_store(pmResult *result, pmdaExt *pmda) +{ + int i, sts; + int context = pmda->e_context; + + check_processes(context); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "bash_store called (%d)", result->numpmid); + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "bash_store_metric called"); + if ((sts = bash_store_metric(vsp, context)) < 0) + return sts; + } + return 0; +} + +static void +bash_end_contextCallBack(int context) +{ + pmdaEventEndClient(context); +} + +static void +timer_expired(int sig, void *ptr) +{ + bash_interval_expired = 1; +} + +void +bash_main(pmdaInterface *dispatch) +{ + fd_set fds, readyfds; + int maxfd, nready, pmcdfd; + + pmcdfd = __pmdaInFd(dispatch); + maxfd = pmcdfd; + + FD_ZERO(&fds); + FD_SET(pmcdfd, &fds); + + /* arm interval timer */ + if (__pmAFregister(&bash_interval, NULL, timer_expired) < 0) { + __pmNotifyErr(LOG_ERR, "registering event interval handler"); + exit(1); + } + + for (;;) { + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, NULL); + if (pmDebug & DBG_TRACE_APPL2) + __pmNotifyErr(LOG_DEBUG, "select: nready=%d interval=%d", + nready, bash_interval_expired); + if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } else if (!bash_interval_expired) { + continue; + } + } + + __pmAFblock(); + if (nready > 0 && FD_ISSET(pmcdfd, &readyfds)) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd PDU [fd=%d]", pmcdfd); + if (__pmdaMainPDU(dispatch) < 0) { + __pmAFunblock(); + exit(1); /* fatal if we lose pmcd */ + } + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "completed pmcd PDU [fd=%d]", pmcdfd); + } + if (bash_interval_expired) { + bash_interval_expired = 0; + event_refresh(indoms[BASH_INDOM].it_indom); + } + __pmAFunblock(); + } +} + +void +bash_init(pmdaInterface *dp) +{ + __pmSetProcessIdentity(username); + + if (dp->status != 0) + return; + + dp->version.four.fetch = bash_fetch; + dp->version.four.store = bash_store; + dp->version.four.instance = bash_instance; + pmdaSetFetchCallBack(dp, bash_fetchCallBack); + pmdaSetEndContextCallBack(dp, bash_end_contextCallBack); + pmdaInit(dp, indoms, INDOM_COUNT, metrics, METRIC_COUNT); + + event_init(); +} + +static void +convertUnits(char **endnum, long *maxmem) +{ + switch ((int) **endnum) { + case 'b': + case 'B': + break; + case 'k': + case 'K': + *maxmem *= 1024; + break; + case 'm': + case 'M': + *maxmem *= 1024 * 1024; + break; + case 'g': + case 'G': + *maxmem *= 1024 * 1024 * 1024; + break; + } + (*endnum)++; +} + +int +main(int argc, char **argv) +{ + static char helppath[MAXPATHLEN]; + char *endnum; + pmdaInterface desc; + long minmem; + int c, sep = __pmPathSeparator(); + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + minmem = getpagesize(); + bash_maxmem = (minmem > DEFAULT_MAXMEM) ? minmem : DEFAULT_MAXMEM; + snprintf(helppath, sizeof(helppath), "%s%c" "bash" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_5, pmProgname, BASH, "bash.log", helppath); + + while ((c = pmdaGetOptions(argc, argv, &opts, &desc)) != EOF) { + switch (c) { + case 'm': + bash_maxmem = strtol(opts.optarg, &endnum, 10); + if (*endnum != '\0') + convertUnits(&endnum, &bash_maxmem); + if (*endnum != '\0' || bash_maxmem < minmem) { + pmprintf("%s: invalid max memory '%s' (min=%ld)\n", + pmProgname, opts.optarg, minmem); + opts.errors++; + } + break; + + case 's': + if (pmParseInterval(opts.optarg, &bash_interval, &endnum) < 0) { + pmprintf("%s: -s requires a time interval: %s\n", + pmProgname, endnum); + free(endnum); + opts.errors++; + } + break; + } + } + + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + if (opts.username) + username = opts.username; + + pmdaOpenLog(&desc); + bash_init(&desc); + pmdaConnect(&desc); + bash_main(&desc); + exit(0); +} diff --git a/src/pmdas/bash/bashproc.sh b/src/pmdas/bash/bashproc.sh new file mode 100755 index 0000000..4d867a7 --- /dev/null +++ b/src/pmdas/bash/bashproc.sh @@ -0,0 +1,64 @@ +# Common bash(1) procedures to be used in the Performance Co-Pilot +# Bash PMDA trace instrumentation mechanism +# +# Copyright (c) 2012 Nathan Scott. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. + +pcp_trace() +{ + command="$1" + shift + + case "$command" + in + setup|init) + [ -z "${PCP_TMP_DIR}" ] && return 0 # incorrect call sequence + trap "pcp_trace off" 0 + export PCP_TRACE_DIR="${PCP_TMP_DIR}/pmdabash" + export PCP_TRACE_HEADER="${PCP_TRACE_DIR}/.$$" + export PCP_TRACE_DATA="${PCP_TRACE_DIR}/$$" + ;; + + stop|off) + exec 99>/dev/null + # unlink is performed by pmdabash to resolve race conditions + unset BASH_XTRACEFD PCP_TRACE_DIR PCP_TRACE_HEADER PCP_TRACE_DATA + ;; + + start|on) + # series of sanity checks first + [ -n "${BASH_VERSION}" ] || return 0 # wrong shell + [ "${BASH_VERSINFO[0]}" -ge 4 ] || return 0 # no support + [ "${BASH_VERSINFO[0]}" -ne 4 -o "${BASH_VERSINFO[1]}" -ge 2 ] || \ + return 0 # no support + [ -z "${PCP_TMP_DIR}" ] && return 0 # incorrect setup + [ -z "${PCP_TRACE_DIR}" ] && pcp_trace init $@ # not yet tracing + [ -d "${PCP_TRACE_DIR}" ] || return 0 # no pcp pmda yet + + # is this the child of a traced shell? + [ -e /proc/self/fd/99 ] && pcp_trace init $@ + + trap "pcp_trace on" 13 # reset on sigpipe (consumer died) + printf -v PCP_START '%(%s)T' -2 + mkfifo -m 600 "${PCP_TRACE_DATA}" 2>/dev/null || return 0 + # header: version, command, parent, and start time + echo "version:1 ppid:${PPID} date:${PCP_START} + $@" \ + > "${PCP_TRACE_HEADER}" || return 0 + # setup link between xtrace & fifo + exec 99>"${PCP_TRACE_DATA}" + BASH_XTRACEFD=99 # magic bash environment variable + set -o xtrace # start tracing from here onward + # traces: time, line#, and (optionally) function + PS4='time:${SECONDS} line:${LINENO} func:${FUNCNAME[0]-} + ' + ;; + esac +} diff --git a/src/pmdas/bash/event.c b/src/pmdas/bash/event.c new file mode 100644 index 0000000..6129e5a --- /dev/null +++ b/src/pmdas/bash/event.c @@ -0,0 +1,434 @@ +/* + * Event support for the bash tracing PMDA + * + * Copyright (c) 2012 Nathan Scott. All rights reversed. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include "event.h" +#include "pmda.h" + +static char *prefix = "pmdabash"; +static char *pcptmpdir; /* probably /var/tmp */ +static char pidpath[MAXPATHLEN]; + +/* + * Extract time of creation of the trace files. Initially uses header file + * as a reference since its created initially and then never modified again. + * Subsequent calls will use last modification to the trace data file. + */ +void +process_stat_timestamp(bash_process_t *process, struct timeval *timestamp) +{ +#if defined(HAVE_ST_MTIME_WITH_E) && defined(HAVE_STAT_TIME_T) + timestamp->tv_sec = process->stat.st_mtime.tv_sec; + timestamp->tv_usec = process->stat.st_mtime.tv_nsec / 1000; +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + timestamp->tv_sec = process->stat.st_mtimespec.tv_sec; + timestamp->tv_usec = process->stat.st_mtimespec.tv_nsec / 1000; +#elif defined(HAVE_STAT_TIMESTRUC) || defined(HAVE_STAT_TIMESPEC) || defined(HAVE_STAT_TIMESPEC_T) + timestamp->tv_sec = process->stat.st_mtim.tv_sec; + timestamp->tv_usec = process->stat.st_mtim.tv_nsec / 1000; +#else +!bozo! +#endif +} + +/* + * Parse the header file (/path/.pid) containing xtrace metadata. + * Helper routine, used during initialising of a tracked shell. + */ +static void +process_head_parser(bash_process_t *verify, const char *buffer, size_t size) +{ + char *p = (char *)buffer, *end = (char *)buffer + size - 1; + char script[1024]; + int version = 0; + int date = 0; + + p += extract_int(p, "version:", sizeof("version:")-1, &version); + p += extract_int(p, "ppid:", sizeof("ppid:")-1, &verify->parent); + p += extract_int(p, "date:", sizeof("date:")-1, &date); + extract_cmd(p, end - p, "+", sizeof("+")-1, script, sizeof(script)); + + if (date) { + /* Use the given starttime of the script from the header */ + verify->starttime.tv_sec = date; + verify->starttime.tv_usec = 0; + } else { + /* Use a timestamp from the header as a best-effort guess */ +#if defined(HAVE_ST_MTIME_WITH_E) && defined(HAVE_STAT_TIME_T) + verify->starttime.tv_sec = verify->stat.st_mtime.tv_sec; + verify->starttime.tv_usec = verify->stat.st_mtime.tv_nsec / 1000; +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + verify->starttime.tv_sec = verify->stat.st_mtimespec.tv_sec; + verify->starttime.tv_usec = verify->stat.st_mtimespec.tv_nsec / 1000; +#elif defined(HAVE_STAT_TIMESTRUC) || defined(HAVE_STAT_TIMESPEC) || defined(HAVE_STAT_TIMESPEC_T) + verify->starttime.tv_sec = verify->stat.st_mtim.tv_sec; + verify->starttime.tv_usec = verify->stat.st_mtim.tv_nsec / 1000; +#else +!bozo! +#endif + } + verify->version = version; + + size = 16 + strlen(script); /* pid and script name */ + verify->instance = malloc(size); + snprintf(verify->instance, size, "%u %s", verify->pid, script); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process header v%d: inst='%s' ppid=%d", + verify->version, verify->instance, verify->parent); +} + +/* + * Verify the header file (/path/.pid) containing xtrace metadata. + * Helper routine, used during initialising of a tracked shell. + */ +static int +process_head_verify(const char *filename, bash_process_t *verify) +{ + size_t size; + char buffer[1024]; + int fd = open(filename, O_RDONLY); + + if (fd < 0) + return fd; + if (fstat(fd, &verify->stat) < 0 || !S_ISREG(verify->stat.st_mode)) { + close(fd); + return -1; + } + + size = read(fd, buffer, sizeof(buffer)); + if (size > 0) + process_head_parser(verify, buffer, size); + close(fd); + + /* make sure we only parse header/trace file formats we understand */ + if (verify->version < MINIMUM_VERSION || verify->version > MAXIMUM_VERSION) + return -1; + return 0; +} + +/* + * Verify the data files associated with a traced bash process. + * Helper routine, used during initialising of a tracked shell. + */ +static int +process_verify(const char *bashname, bash_process_t *verify) +{ + int fd; + char *endnum; + char path[MAXPATHLEN]; + struct stat stat; + + verify->pid = (pid_t) strtoul(bashname, &endnum, 10); + if (*endnum != '\0' || verify->pid < 1) + return -1; + + snprintf(path, sizeof(path), "%s%c.%s", pidpath, __pmPathSeparator(), bashname); + if (process_head_verify(path, verify) < 0) + return -1; + + snprintf(path, sizeof(path), "%s%c%s", pidpath, __pmPathSeparator(), bashname); + if ((fd = open(path, O_RDONLY | O_NONBLOCK)) < 0) + return -1; + if (fstat(fd, &stat) < 0 || !S_ISFIFO(stat.st_mode)) { + close(fd); + return -1; + } + verify->fd = fd; + return 0; +} + +/* + * Finally allocate memory for a verified, traced bash process. + * Helper routine, used during initialising of a tracked shell. + */ +static bash_process_t * +process_alloc(const char *bashname, bash_process_t *init, int numclients) +{ + int queueid = pmdaEventNewActiveQueue(bashname, bash_maxmem, numclients); + bash_process_t *bashful = malloc(sizeof(bash_process_t)); + + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "process_alloc: %s, queueid=%d", bashname, queueid); + + if (!bashful) { + __pmNotifyErr(LOG_ERR, "process allocation out of memory"); + return NULL; + } + if (queueid < 0) { + __pmNotifyErr(LOG_ERR, "attempt to dup queue for %s", bashname); + free(bashful); + return NULL; + } + + /* Tough access situation - how to log without this? */ + pmdaEventSetAccess(pmdaGetContext(), queueid, 1); + + bashful->fd = init->fd; + bashful->pid = init->pid; + bashful->parent = init->parent; + bashful->queueid = queueid; + bashful->exited = 0; + bashful->finished = 0; + bashful->noaccess = 0; + bashful->version = init->version; + bashful->padding = 0; + + memcpy(&bashful->starttime, &init->starttime, sizeof(struct timeval)); + memcpy(&bashful->stat, &init->stat, sizeof(struct stat)); + /* copy of first stat time, identifies first event */ + process_stat_timestamp(bashful, &bashful->startstat); + /* copy of pointer to dynamically allocated memory */ + bashful->instance = init->instance; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process_alloc: %s", bashful->instance); + + return bashful; +} + +int +event_start(bash_process_t *bp, struct timeval *timestamp) +{ + int start = memcmp(timestamp, &bp->startstat, sizeof(*timestamp)); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "check start event for %s (%d), %ld vs %ld", + bp->instance, start, bp->startstat.tv_sec, timestamp->tv_sec); + + return start == 0; +} + +/* + * Initialise a bash process data structure using the header and + * trace file. Ready to accept event traces from this shell on + * completion of this routine - file descriptor setup, structure + * filled with all metadata (exported) about this process. + * Note: this is using an on-stack process structure, only if it + * all checks out will we allocate memory for it, and keep it. + */ +static int +process_init(const char *bashname, bash_process_t **bp) +{ + bash_process_t init = { 0 }; + + pmAtomValue atom; + pmdaEventClients(&atom); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process_init: %s (%d clients)", + bashname, atom.ul); + + if (process_verify(bashname, &init) < 0) + return -1; + *bp = process_alloc(bashname, &init, atom.ul); + if (*bp == NULL) + return -1; + return 0; +} + +static int +process_read(bash_process_t *process) +{ + int j; + char *s, *p; + size_t offset; + ssize_t bytes; + struct timeval timestamp; + + static char *buffer; + static int bufsize; + + /* + * Using a static (global) event buffer to hold initial read. + * The aim is to reduce memory allocation until we know we'll + * need to keep something. + */ + if (!buffer) { + int sts = 0; + bufsize = 16 * getpagesize(); +#ifdef HAVE_POSIX_MEMALIGN + sts = posix_memalign((void **)&buffer, getpagesize(), bufsize); +#else +#ifdef HAVE_MEMALIGN + buffer = (char *)memalign(getpagesize(), bufsize); + if (buffer == NULL) sts = -1; +#else + buffer = (char *)malloc(bufsize); + if (buffer == NULL) sts = -1; +#endif +#endif + if (sts != 0) { + __pmNotifyErr(LOG_ERR, "event buffer allocation failure"); + return -1; + } + } + + offset = 0; +multiread: + if (process->fd < 0) + return 0; + + bytes = read(process->fd, buffer + offset, bufsize - 1 - offset); + + /* + * Ignore the error if: + * - we've got EOF (0 bytes read) + * - EAGAIN/EWOULDBLOCK (fd is marked nonblocking and read would block) + */ + if (bytes == 0) + return 0; + if (bytes < 0 && (errno == EBADF)) + return 0; + if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return 0; + if (bytes > bash_maxmem) + return 0; + if (bytes < 0) { + __pmNotifyErr(LOG_ERR, "read failure on process %s: %s", + process->instance, strerror(errno)); + return -1; + } + + process_stat_timestamp(process, ×tamp); + + buffer[bufsize-1] = '\0'; + for (s = p = buffer, j = 0; *s != '\0' && j < bufsize-1; s++, j++) { + if (*s != '\n') + continue; + *s = '\0'; + bytes = (s+1) - p; + pmdaEventQueueAppend(process->queueid, p, bytes, ×tamp); + p = s + 1; + } + /* did we just do a full buffer read? */ + if (p == buffer) { + char msg[64]; + __pmNotifyErr(LOG_ERR, "Ignoring long (%d bytes) line: \"%s\"", (int) + bytes, __pmdaEventPrint(p, bytes, msg, sizeof(msg))); + } else if (j == bufsize - 1) { + offset = bufsize-1 - (p - buffer); + memmove(buffer, p, offset); + goto multiread; /* read rest of line */ + } + return 1; +} + +static void +process_unlink(bash_process_t *process, const char *bashname) +{ + char path[MAXPATHLEN]; + + snprintf(path, sizeof(path), "%s%c%s", pidpath, __pmPathSeparator(), bashname); + unlink(path); + snprintf(path, sizeof(path), "%s%c.%s", pidpath, __pmPathSeparator(), bashname); + unlink(path); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process_unlink: removed %s", bashname); +} + +static int +process_drained(bash_process_t *process) +{ + pmAtomValue value = { 0 }; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process_queue_drained check on queue %d (pid %d)", + process->queueid, process->pid); + if (pmdaEventQueueMemory(process->queueid, &value) < 0) + return 1; /* error, consider it drained and cleanup */ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process_queue_drained: %s (%llu)", value.ll?"n":"y", (long long)value.ull); + return value.ull == 0; +} + +static void +process_done(bash_process_t *process, const char *bashname) +{ + struct timeval timestamp; + + if (process->exited == 0) { + process->exited = (__pmProcessExists(process->pid) == 0); + + if (!process->exited) + return; + /* empty event inserted into queue to denote bash has exited */ + if (!process->finished) { + process->finished = 1; /* generate no further events */ + process_stat_timestamp(process, ×tamp); + pmdaEventQueueAppend(process->queueid, NULL, 0, ×tamp); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "process_done: marked queueid %d (pid %d) done", + process->queueid, process->pid); + } + } + + if (process->finished) { + /* once all clients have seen final events, clean named queue */ + if (process_drained(process)) + process_unlink(process, bashname); + } +} + +void +event_refresh(pmInDom bash_indom) +{ + struct dirent **files; + bash_process_t *bp; + int i, id, sts, num = scandir(pidpath, &files, NULL, NULL); + + if (pmDebug & DBG_TRACE_APPL0 && num > 2) + __pmNotifyErr(LOG_DEBUG, "event_refresh: phase1: %d files", num - 2); + + pmdaCacheOp(bash_indom, PMDA_CACHE_INACTIVE); + + /* (re)activate processes that are actively generating events */ + for (i = 0; i < num; i++) { + char *processid = files[i]->d_name; + + if (processid[0] == '.') + continue; + + /* either create or re-activate a bash process structure */ + sts = pmdaCacheLookupName(bash_indom, processid, &id, (void **)&bp); + if (sts != PMDA_CACHE_INACTIVE) { + if (process_init(processid, &bp) < 0) + continue; + } + pmdaCacheStore(bash_indom, PMDA_CACHE_ADD, bp->instance, (void *)bp); + + /* read any/all new events for this bash process, enqueue 'em */ + process_read(bp); + + /* check if process is running and generate end marker if not */ + process_done(bp, files[i]->d_name); + } + + for (i = 0; i < num; i++) + free(files[i]); + if (num > 0) + free(files); +} + +void +event_init(void) +{ + pcptmpdir = pmGetConfig("PCP_TMP_DIR"); + sprintf(pidpath, "%s%c%s", pcptmpdir, __pmPathSeparator(), prefix); +} diff --git a/src/pmdas/bash/event.h b/src/pmdas/bash/event.h new file mode 100644 index 0000000..f36b5b4 --- /dev/null +++ b/src/pmdas/bash/event.h @@ -0,0 +1,65 @@ +/* + * Event support for the bash tracing PMDA + * + * Copyright (c) 2012 Nathan Scott. All rights reversed. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _EVENT_H +#define _EVENT_H + +#include +#include +#include "pmapi.h" +#include "impl.h" + +typedef struct bash_process { + int fd; + pid_t pid; + pid_t parent; + int queueid; + + int exited : 1; /* flag: process running? */ + int finished: 1; /* flag: exit event sent? */ + int noaccess: 1; /* flag: store-to-access? */ + int version : 8; /* pmda <-> bash xtrace version */ + int padding : 21; /* filler */ + + struct timeval starttime; /* timestamp trace started */ + struct timeval startstat; /* timestamp of first stat */ + struct stat stat; /* stat of the header file */ + + char *instance; /* process id, space, script */ +} bash_process_t; + +typedef struct bash_trace { + int flags; + struct timeval timestamp; + int line; + char function[64]; + char command[512]; +} bash_trace_t; + +extern long bash_maxmem; + +extern void event_init(void); +extern void event_refresh(pmInDom); +extern int event_start(bash_process_t *, struct timeval *); +extern void process_stat_timestamp(bash_process_t *, struct timeval *); + +#define MINIMUM_VERSION 1 +#define MAXIMUM_VERSION 1 + +extern int extract_int(char *, const char *, size_t, int *); +extern int extract_str(char *, size_t, const char *, size_t, char *, size_t); +extern int extract_cmd(char *, size_t, const char *, size_t, char *, size_t); + +#endif /* _EVENT_H */ diff --git a/src/pmdas/bash/help b/src/pmdas/bash/help new file mode 100644 index 0000000..953d214 --- /dev/null +++ b/src/pmdas/bash/help @@ -0,0 +1,48 @@ +# +# Copyright (c) 2012 Nathan Scott. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# bash PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ BASH.0 Instance domain of traced bash processes +An instance domain representing all of the currently running bash +processes which are exporting trace metrics. + +@ bash.xtrace.numclients +The number of attached clients. + +@ bash.xtrace.maxmem +Maximum number of queued event bytes for each log file. + +@ bash.xtrace.queuemem +@ bash.xtrace.count +@ bash.xtrace.records +@ bash.xtrace.parameters.pid +@ bash.xtrace.parameters.parent +@ bash.xtrace.parameters.lineno +@ bash.xtrace.parameters.function +@ bash.xtrace.parameters.command + diff --git a/src/pmdas/bash/pcp.sh b/src/pmdas/bash/pcp.sh new file mode 100755 index 0000000..cef1e11 --- /dev/null +++ b/src/pmdas/bash/pcp.sh @@ -0,0 +1,30 @@ +# Shell interface to PCP shell event tracing PMDA + +if [ -z "$PCP_SH_DONE" ] +then + if [ -n "$PCP_CONF" ] + then + __CONF="$PCP_CONF" + elif [ -n "$PCP_DIR" ] + then + __CONF="$PCP_DIR/etc/pcp.conf" + else + __CONF=/etc/pcp.conf + fi + if [ ! -f "$__CONF" ] + then + echo "pcp.env: Fatal Error: \"$__CONF\" not found" >&2 + exit 1 + fi + eval `sed -e 's/"//g' $__CONF \ + | awk -F= ' +/^PCP_/ && NF == 2 { + exports=exports" "$1 + printf "%s=${%s:-\"%s\"}\n", $1, $1, $2 +} END { + print "export", exports +}'` + export PCP_ENV_DONE=y +fi + +. $PCP_SHARE_DIR/lib/bashproc.sh diff --git a/src/pmdas/bash/pmns b/src/pmdas/bash/pmns new file mode 100644 index 0000000..a26790c --- /dev/null +++ b/src/pmdas/bash/pmns @@ -0,0 +1,36 @@ +/* + * Metrics for bash xtrace PMDA + * + * Copyright (c) 2012 Nathan Scott. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +bash { + xtrace +} + +bash.xtrace { + numclients BASH:0:0 + maxmem BASH:0:1 + queuemem BASH:0:2 + count BASH:0:3 + records BASH:0:4 + parameters +} + +bash.xtrace.parameters { + pid BASH:0:5 + parent BASH:0:6 + lineno BASH:0:7 + function BASH:0:8 + command BASH:0:9 +} diff --git a/src/pmdas/bash/root b/src/pmdas/bash/root new file mode 100644 index 0000000..2e66cb4 --- /dev/null +++ b/src/pmdas/bash/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { bash } + +#include "pmns" + diff --git a/src/pmdas/bash/test-child.sh b/src/pmdas/bash/test-child.sh new file mode 100755 index 0000000..2b5d482 --- /dev/null +++ b/src/pmdas/bash/test-child.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2012 Nathan Scott. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.sh +pcp_trace on $0 $@ + +wired() +{ + # burn a little CPU, then sleep + for i in 0 1 2 3 4 5 6 7 8 9 0 + do + /bin/true && /bin/true + done + sleep $1 +} + +count=0 +while true +do + (( count++ )) + echo "get busy, $count" # top level + wired 2 # call a shell function + [ $count -ge 10 ] && break +done + +pcp_trace off +exit 0 diff --git a/src/pmdas/bash/test-trace.sh b/src/pmdas/bash/test-trace.sh new file mode 100755 index 0000000..6ff89b7 --- /dev/null +++ b/src/pmdas/bash/test-trace.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2012 Nathan Scott. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.sh +pcp_trace on $0 $@ + +tired() +{ + sleep $1 +} + +count=0 +while true +do + (( count++ )) + echo "awoke, $count" # top level + tired 2 # call a shell function + branch=$(( count % 3 )) + case $branch + in + 0) ./test-child.sh $count & + ;; + 2) wait + ;; + esac +done + +pcp_trace off +exit 0 diff --git a/src/pmdas/bash/util.c b/src/pmdas/bash/util.c new file mode 100644 index 0000000..3284579 --- /dev/null +++ b/src/pmdas/bash/util.c @@ -0,0 +1,84 @@ +/* + * Utility routines for the bash tracing PMDA + * + * Copyright (c) 2012 Nathan Scott. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "event.h" +#include "pmda.h" +#include + +int +extract_int(char *s, const char *field, size_t length, int *value) +{ + char *endnum; + int num; + + if (strncmp(s, field, length) != 0) + return 0; + num = strtol(s + length, &endnum, 10); + if (*endnum != ',' && *endnum != '\0' && !isspace((int)*endnum)) + return 0; + *value = num; + return endnum - s + 1; +} + +int +extract_str(char *s, size_t end, const char *field, size_t length, char *value, size_t vsz) +{ + char *p; + + if (strncmp(s, field, length) != 0) + return 0; + p = s + length; + while (*p != ',' && *p != '\0' && !isspace((int)*p)) + p++; + *p = '\0'; + strncpy(value, s + length, vsz); + return p - s + 1; +} + +int +extract_cmd(char *s, size_t end, const char *field, size_t length, char *value, size_t vsz) +{ + char *start = NULL, *stop = NULL, *p; + int len; + + /* find the start of the command */ + for (p = s; p < s + end; p++) { + if (strncmp(p, field, length) != 0) + continue; + p++; + if (*p == ' ') + p++; + break; + } + if (p == s + end) + return 0; + start = p; + + /* find the command terminator */ + while (*p != '\n' && *p != '\0' && p < s + end) + p++; + stop = p; + + /* truncate it if necessary */ + len = stop - start; + if (len > vsz - 1) + len = vsz - 1; + + /* copy it over to "value" */ + start[len] = '\0'; + strncpy(value, start, len + 1); + return len; +} diff --git a/src/pmdas/bonding/GNUmakefile b/src/pmdas/bonding/GNUmakefile new file mode 100644 index 0000000..be7f4b6 --- /dev/null +++ b/src/pmdas/bonding/GNUmakefile @@ -0,0 +1,56 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = bonding +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) +else +install: +endif + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/bonding/Install b/src/pmdas/bonding/Install new file mode 100755 index 0000000..1abb843 --- /dev/null +++ b/src/pmdas/bonding/Install @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Install the Bonding PMDA (bonded network interfaces) +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=bonding +perl_opt=true +daemon_opt=false +forced_restart=false + +if ! test -d /sys/class/net; then + echo "SYSFS not enabled in your kernel" + exit 1 +fi +if ! test -f /sys/class/net/bonding_masters; then + echo "Bonding statistics unavailable (load bonding module)" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/bonding/Remove b/src/pmdas/bonding/Remove new file mode 100755 index 0000000..04385a5 --- /dev/null +++ b/src/pmdas/bonding/Remove @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the bonding PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=bonding + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/bonding/pmdabonding.pl b/src/pmdas/bonding/pmdabonding.pl new file mode 100644 index 0000000..3a0a0e5 --- /dev/null +++ b/src/pmdas/bonding/pmdabonding.pl @@ -0,0 +1,154 @@ +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +my $pmda = PCP::PMDA->new('bonding', 96); +my $sysfs = '/sys/class/net/'; + +sub bonding_interface_check +{ + my @interfaces = (); + my $instanceid = 0; + + if (open(BONDS, $sysfs . 'bonding_masters')) { + my @bonds = split / /, ; + foreach my $bond (@bonds) { + chomp $bond; + push @interfaces, $instanceid++, $bond; + } + close BONDS; + } + $pmda->replace_indom(0, \@interfaces); +} + +sub bonding_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ($path, $name, $value, $fh, @vals); + + #$pmda->log("bonding_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + if (!defined($metric_name)) { return (PM_ERR_PMID, 0); } + + # special case: failures count from /proc (no sysfs equivalent) + if ($item == 7) { + $value = 0; + $name = '/proc/net/bonding/' . 'bond' . $inst; + open($fh, $name) || return (PM_ERR_APPVERSION, 0); + while (<$fh>) { + if (m/^Link Failure Count: (\d+)$/) { $value += $1; } + } + close $fh; + } else { + $name = $metric_name; + $path = $sysfs . 'bond' . $inst . '/bonding/'; + $name =~ s/^bonding\./$path/; + + # special case: mode contains two values (name and type) + if ($item == 5) { $name =~ s/\.type$//; } + if ($item == 6) { $name =~ s/\.name$//; } + + open($fh, $name) || return (PM_ERR_APPVERSION, 0); + $value = <$fh>; + close $fh; + + if (!defined($value)) { return (PM_ERR_APPVERSION, 0); } + if ($item == 5) { @vals = split / /, $value; $value = $vals[1]; } + if ($item == 6) { @vals = split / /, $value; $value = $vals[0]; } + chomp $value; + } + + return ($value, 1); +} + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_STRING, 0, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), "bonding.slaves", '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_STRING, 0, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), "bonding.active_slave", '', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, 0, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), "bonding.use_carrier", '', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, 0, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), "bonding.updelay", '', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U32, 0, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), "bonding.downdelay", '', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, 0, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), "bonding.mode.type", '', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_STRING, 0, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), "bonding.mode.name", '', ''); +$pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U32, 0, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), "bonding.failures", '', ''); + +$pmda->add_indom(0, [], '', ''); + +$pmda->set_fetch(\&bonding_interface_check); +$pmda->set_instance(\&bonding_interface_check); +$pmda->set_fetch_callback(\&bonding_fetch_callback); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdabonding - Linux bonded interface performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +exports metric values from bonded network interfaces in the Linux +kernel. + +=head1 INSTALLATION + +If you want access to the names and values for the bonding performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/bonding + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/bonding + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/bonding/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/bonding/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/bonding.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and ifenslave(8). diff --git a/src/pmdas/cisco/Cisco.pmchart b/src/pmdas/cisco/Cisco.pmchart new file mode 100644 index 0000000..6baf4e7 --- /dev/null +++ b/src/pmdas/cisco/Cisco.pmchart @@ -0,0 +1,11 @@ +#pmchart +Version 1.2 host dynamic + +Chart Style plot + Plot Color #-cycle Host * Metric cisco.rate_in + +Chart Style plot + Plot Color #-cycle Host * Metric cisco.rate_out + +# +# Created Mon Sep 1 11:34:14 1997 diff --git a/src/pmdas/cisco/GNUmakefile b/src/pmdas/cisco/GNUmakefile new file mode 100644 index 0000000..e6c2733 --- /dev/null +++ b/src/pmdas/cisco/GNUmakefile @@ -0,0 +1,70 @@ +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = cisco +DOMAIN = CISCO +TARGETS = $(IAM)$(EXECSUFFIX) probe$(EXECSUFFIX) +CFILES = cisco.c pmda.c interface.c telnet.c +HFILES = cisco.h +DFILES = README +LSRCFILES=Install Remove root pmns Cisco.pmchart help Samples Tested $(DFILES) \ + cisco.in_util.pmie cisco.out_util.pmie probe.c parse.sh + +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_PTHREADS) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMCHART = $(PCP_VAR_DIR)/config/pmchart +PMIEDIR = $(PCP_VAR_DIR)/config/pmieconf/$(IAM) + +LDIRT = domain.h cisco.log *.dir *.pag so_locations a.out probe.o $(TARGETS) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(TARGETS) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 parse.sh $(PMDADIR)/parse + $(INSTALL) -m 755 probe Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns domain.h $(PMDADIR) + $(INSTALL) -m 644 Cisco.pmchart $(PMCHART)/Cisco + $(INSTALL) -m 755 -d $(PMIEDIR) + $(INSTALL) -m 644 cisco.in_util.pmie $(PMIEDIR)/in_util + $(INSTALL) -m 644 cisco.out_util.pmie $(PMIEDIR)/out_util +else +build-me: +install: +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +probe$(EXECSUFFIX): probe.o interface.o telnet.o + $(CCF) -o $@ $(LDFLAGS) probe.o interface.o telnet.o $(LDLIBS) + +cisco.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/cisco/Install b/src/pmdas/cisco/Install new file mode 100644 index 0000000..5ee4742 --- /dev/null +++ b/src/pmdas/cisco/Install @@ -0,0 +1,156 @@ +#! /bin/sh +# +# Copyright (c) 1997-2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the cisco PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=cisco +pmda_interface=2 +forced_restart=false + +# Do it +# +pmdaSetup + +check_delay=20 +pollrate=120 # poll each Cisco interface once every 120 seconds + # -- change this if desired + +# special cisco PMDA args + +if $do_pmda +then + args="-r$pollrate" + default="wanptg" + while true + do + no_host=true + while $no_host + do + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Cisco hostname or IP address? [return to quit Cisco selection] ""$PCP_ECHO_C" + read host + [ "X$host" = X ] && break + echo ' +A username and/or user-level password may be required for the Cisco +"show interface" command. + If you are unsure, try the command + $ telnet '$host' + and if the prompt "Username:" appears, then a username is required, + and if the prompt "Password:" appears, a user-level password is required, + otherwise respond with an empty line for the next two questions. + Once logged in, we need to know the termination string for the command + line prompt (one or more unique characters at the end of the prompt) - + the default is ">", but if this is not correct, enter the prompt + termination string also. +' + $PCP_ECHO_PROG $PCP_ECHO_N "Cisco username? ""$PCP_ECHO_C" + read username + userarg="" + [ "X$username" != X ] && userarg="-U$username" + $PCP_ECHO_PROG $PCP_ECHO_N "User-level Cisco password? ""$PCP_ECHO_C" + read passwd + passarg="" + [ "X$passwd" != X ] && passarg="-P$passwd" + $PCP_ECHO_PROG $PCP_ECHO_N "Cisco command line prompt termination? [>] ""$PCP_ECHO_C" + read prompt + promptarg="" + [ "X$prompt" != X ] && promptarg="-s'$prompt'" + echo "Probing Cisco for list of interfaces ..." + for try in 1 2 3 + do + intf=`eval ./probe $userarg $passarg $promptarg $host 2>$tmp/err` + [ ! -z "$intf" ] && break + sleep 2 + done + if [ -z "$intf" ] + then + echo ' +There appears to be a problem ... after three attempts could not get +interfaces. Output at the last attempt was:' + sed -e 's/^/ /' <$tmp/err + echo ' +You may wish to try the following commands to identify the configured +interfaces for this Cisco. + $ telnet '$host' + ....> terminal length 0 + ....> show interface + ....> quit +' + else + no_host=false + fi + done + [ "X$host" = X ] && break + + if [ -z "$username" ] + then + login="" + else + login="@$username" + fi + [ ! -z "$passwd" ] && login="$login?$passwd" + [ ! -z "$prompt" ] && login="$login!$prompt" + + echo ' +Enter interfaces to monitor, one per line in the format tX where "t" is +a type and one of "e" (Ethernet), "E" (FastEthernet), "f" (Fddi), "s" +(Serial), "a" (ATM), "B" (ISDN BRI) or "h" (HSSC) and "X" is an +interface identifier which is either an integer (e.g. 4000 Series +routers) or two integers separated by a slash (e.g. 7000 Series +routers).' + + while true + do + echo + echo 'The currently unselected interfaces for the Cisco "'$host'" are:' + echo "$intf" | fmt | sed -e 's/^/ /' + echo 'Enter "*" to select all, "quit" to terminate selections for this Cisco.' + + first=`echo "$intf" | sed -e 's/ .*//'` + [ -z "$first" ] && first=quit + $PCP_ECHO_PROG $PCP_ECHO_N "Interface? [$first] ""$PCP_ECHO_C" + read ans + [ "X$ans" = Xquit ] && break + if [ "X$ans" = "X*" ] + then + # do them all + for ans in `echo "$intf"` + do + args="$args $host:$ans$login" + login='' + done + break + fi + [ -z "$ans" ] && ans="$first" + if echo " $intf" | grep " $ans" >/dev/null + then + sed_ans=`echo $ans | sed -e 's;/;\\\\/;g'` + intf=`echo " $intf" | sed -e "s/ $sed_ans//" -e 's/^ //'` + else + echo "Warning: $ans is not in the list, I hope you know what you're doing" + fi + args="$args $host:$ans$login" + login='' + [ -z "$intf" ] && break + done + done +fi + +pmdaInstall + +exit 0 diff --git a/src/pmdas/cisco/README b/src/pmdas/cisco/README new file mode 100644 index 0000000..908c837 --- /dev/null +++ b/src/pmdas/cisco/README @@ -0,0 +1,76 @@ +Performance Co-Pilot PMDA for Monitoring Cisco Routers +====================================================== + +This PMDA is capable of collecting throughput measures from Cisco +routers throughout a network. + +The PMDA needs to be configured to collect performance data from a +designated set of interfaces on nominated Cisco routers. There is one +instance of the metrics for nominated each router-interface pair. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT cisco + +Installation +============ + + + # cd $PCP_PMDAS_DIR/cisco + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + The Cisco PMDA is one that polls the Cisco routers, and caches the + most recent value for the performance metrics. The cached values + are the ones returned via the PMCD to clients requesting Cisco + performance metrics. The default polling rate is once every two + minutes to each Cisco interface being monitored, if you wish to + change this, edit Install and change the value of pollrate near the + start of the script. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + You will be prompted to identify the Cisco routers and + interfaces you wish to monitor. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/cisco + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/cisco.log) should be checked for any warnings + or errors. + + + The configured interfaces on a particular Cisco may be discovered + using the $PCP_PMDAS_DIR/cisco/probe application. See pmdacisco(1) + for more details. + + + The $PCP_PMDAS_DIR/cisco/parse application supports the same + command line options as pmdacisco for identifying interfaces on + Cisco devices and shares the parser for the output from the Cisco + "show interface" command; this application may be used to gain + diagnostic traces of the interaction between the parser code and + the Cisco device in the event that metrics values are not being + returned by the Cisco PMDA. See pmdacisco(1) for more details. diff --git a/src/pmdas/cisco/Remove b/src/pmdas/cisco/Remove new file mode 100644 index 0000000..0f4f109 --- /dev/null +++ b/src/pmdas/cisco/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the cisco PMDA and/or PMNS +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=cisco + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/cisco/Samples b/src/pmdas/cisco/Samples new file mode 100644 index 0000000..dd25be6 --- /dev/null +++ b/src/pmdas/cisco/Samples @@ -0,0 +1,291 @@ + +Serial 0 is up, line protocol is up + Hardware is MK5025 + Description: frame-relay to MtVA, Tokyo, Hong Kong + Internet address is 155.11.201.172, subnet mask is 255.255.255.128 + MTU 1500 bytes, BW 64 Kbit, DLY 20000 usec, rely 255/255, load 15/255 + Encapsulation FRAME-RELAY, loopback not set, keepalive set (10 sec) + LMI DLCI 0, LMI sent 346516, LMI stat recvd 345070, LMI upd recvd 2 + LMI type is ANSI Annex D + Last input 0:00:01, output 0:00:00, output hang never + Last clearing of "show interface" counters never + Output queue 0/8, 298580 drops; input queue 0/75, 0 drops + Five minute input rate 60000 bits/sec, 16 packets/sec + Five minute output rate 4000 bits/sec, 12 packets/sec + 18139408 packets input, 2763796661 bytes, 0 no buffer + Received 0 broadcasts, 0 runts, 0 giants + 189 input errors, 189 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort + 15860554 packets output, 3098844973 bytes, 0 underruns + 0 output errors, 0 collisions, 668 interface resets, 0 restarts + 686 carrier transitions + +Ethernet 0 is up, line protocol is up + Hardware is Lance, address is 0000.0c03.ed85 (bia 0000.0c03.ed85) + Description: Sydney, Australia + Internet address is 155.11.226.3, subnet mask is 255.255.255.128 + MTU 1500 bytes, BW 10000 Kbit, DLY 1000 usec, rely 255/255, load 1/255 + Encapsulation ARPA, loopback not set, keepalive set (10 sec) + ARP type: ARPA, ARP Timeout 4:00:00 + Last input 0:00:00, output 0:00:00, output hang never + Last clearing of "show interface" counters never + Output queue 0/40, 19188 drops; input queue 1/75, 0 drops + Five minute input rate 5000 bits/sec, 10 packets/sec + Five minute output rate 60000 bits/sec, 13 packets/sec + 12884820 packets input, 1321046112 bytes, 0 no buffer + Received 1570813 broadcasts, 0 runts, 0 giants + 891 input errors, 891 CRC, 634 frame, 0 overrun, 0 ignored, 0 abort + 14717105 packets output, 870622060 bytes, 0 underruns + 98605 output errors, 1883820 collisions, 1 interface resets, 0 restarts + +Fddi2/0 is up, line protocol is up + Hardware is cxBus FDDI, address is 0000.0c38.4360 (bia 0000.0c38.4360) + Internet address is 192.26.80.22, subnet mask is 255.255.255.0 + MTU 4470 bytes, BW 100000 Kbit, DLY 100 usec, rely 255/255, load 1/255 + Encapsulation SNAP, loopback not set, keepalive not set + ARP type: SNAP, ARP Timeout 4:00:00 + Phy-A state is connect, neighbor is unk, cmt signal bits 008/000, status QLS + Phy-B state is active, neighbor is M, cmt signal bits 20C/00E, status ILS + CFM is wrap B, token rotation 5000 usec, ring operational 0:00:58 + Upstream neighbor 0800.6904.155d, downstream neighbor 0040.0b80.a052 + Last input 0:00:00, output 0:00:00, output hang never + Last clearing of "show interface" counters 1w0d + Output queue 0/40, 85 drops; input queue 0/75, 33437 drops + Five minute input rate 160000 bits/sec, 39 packets/sec + Five minute output rate 93000 bits/sec, 108 packets/sec + 33780622 packets input, 4009491185 bytes, 87 no buffer + Received 3768432 broadcasts, 0 runts, 0 giants + 2 input errors, 1 CRC, 1 frame, 0 overrun, 19 ignored, 0 abort + 83194423 packets output, 612803885 bytes, 492 underruns + 0 output errors, 0 collisions, 0 interface resets, 0 restarts + 21602 transitions, 0 traces, 11625 claims, 0 beacon + +Serial1 is up, line protocol is up + Hardware is HD64570 + Description: Frame-relay, MtV, Hong Kong, Sydney + MTU 1500 bytes, BW 1544 Kbit, DLY 20000 usec, rely 255/255, load 5/255 + Encapsulation FRAME-RELAY, loopback not set, keepalive set (10 sec) + LMI enq sent 14998, LMI stat recvd 14885, LMI upd recvd 0, DTE LMI up + LMI enq recvd 0, LMI stat sent 0, LMI upd sent 0 + LMI DLCI 0 LMI type is ANSI Annex D frame relay DTE + Broadcast queue 0/200, broadcasts sent/dropped 508604/0 + Last input 0:00:00, output 0:00:00, output hang never + Last clearing of "show interface" counters never + Output queue 0/40, 947 drops; input queue 0/75, 0 drops + Five minute input rate 117000 bits/sec, 29 packets/sec + Five minute output rate 32000 bits/sec, 20 packets/sec + 2578327 packets input, 1105345262 bytes, 0 no buffer + Received 0 broadcasts, 0 runts, 0 giants + 54 input errors, 4 CRC, 0 frame, 0 overrun, 1 ignored, 4 abort + 1975049 packets output, 675844616 bytes, 0 underruns + 0 output errors, 0 collisions, 142 interface resets, 0 restarts + 3 carrier transitions + DCD=up DSR=up DTR=up RTS=up CTS=up + +Ethernet3/5 is up, line protocol is up + Hardware is cxBus Ethernet, address is 0000.0c38.436d (bia 0000.0c38.436d) + Internet address is 198.29.108.4, subnet mask is 255.255.255.0 + MTU 1500 bytes, BW 10000 Kbit, DLY 1000 usec, rely 255/255, load 2/255 + Encapsulation ARPA, loopback not set, keepalive set (10 sec) + ARP type: ARPA, ARP Timeout 4:00:00 + Last input 0:00:03, output 0:00:01, output hang never + Last clearing of "show interface" counters 6w2d + Output queue 0/40, 54880 drops; input queue 0/75, 1492 drops + 5 minute input rate 1000 bits/sec, 0 packets/sec + 5 minute output rate 91000 bits/sec, 98 packets/sec + 24627812 packets input, 1448819515 bytes, 862 no buffer + Received 3959194 broadcasts, 79 runts, 4 giants + 292 input errors, 195 CRC, 14 frame, 0 overrun, 0 ignored, 0 abort + 0 input packets with dribble condition detected + 150470373 packets output, 3227527737 bytes, 0 underruns + 1274 output errors, 11605581 collisions, 0 interface resets, 0 restarts + +ATM12/0 is up, line protocol is up + Hardware is cxBus ATM + Internet address is 150.166.230.24 255.255.255.0 + MTU 4470 bytes, BW 156250 Kbit, DLY 80 usec, rely 255/255, load 1/255 + Encapsulation ATM, loopback not set, keepalive set (10 sec) + Encapsulation(s): AAL5, PVC mode + 256 TX buffers, 256 RX buffers, 2048 maximum active VCs, 1024 VCs per VP, 15 current VCCs + Last input 0:00:00, output 0:00:00, output hang never + Last clearing of "show interface" counters never + Output queue 0/40, 511 drops; input queue 0/75, 10612 drops + 5 minute input rate 181000 bits/sec, 116 packets/sec + 5 minute output rate 160000 bits/sec, 68 packets/sec + 658262505 packets input, 1239748718 bytes, 199 no buffer + Received 0 broadcasts, 0 runts, 0 giants + 2570 input errors, 2569 CRC, 1 frame, 0 overrun, 0 ignored, 0 abort + 788426419 packets output, 4188341273 bytes, 0 underruns + 0 output errors, 0 collisions, 21 interface resets, 0 restarts + 0 output buffer failures, 63 output buffers swapped out + +Hssi3/0 is administratively down, line protocol is down + Hardware is cxBus HSSI + Description: 45 Meg Microwave Link to Building 27 + Internet address is 150.166.124.1 255.255.255.0 + MTU 4470 bytes, BW 45045 Kbit, DLY 200 usec, rely 255/255, load 1/255 + Encapsulation HDLC, loopback not set, keepalive set (10 sec) + Last input 1:07:12, output 1:07:08, output hang never + Last clearing of "show interface" counters never + Output queue 0/40, 2 drops; input queue 0/75, 37 drops + 5 minute input rate 0 bits/sec, 0 packets/sec + 5 minute output rate 0 bits/sec, 0 packets/sec + 306700515 packets input, 1443052046 bytes, 5 no buffer + Received 1118787 broadcasts, 0 runts, 0 giants + 0 parity + 241 input errors, 0 CRC, 241 frame, 0 overrun, 0 ignored, 0 abort + 246381377 packets output, 3684064154 bytes, 0 underruns + 0 output errors, 0 applique, 210 interface resets, 0 restarts + 0 output buffer failures, 83 output buffers swapped out + 8214 carrier transitions + +FastEthernet1/0 is up, line protocol is up + Hardware is cyBus FastEthernet Interface, address is 0060.3eb0.8c20 (bia 0060.3eb0.8c20) + Internet address is 192.111.17.1 255.255.255.0 + MTU 1500 bytes, BW 100000 Kbit, DLY 100 usec, rely 255/255, load 1/255 + Encapsulation ARPA, loopback not set, keepalive set (10 sec), fdx, MII + ARP type: ARPA, ARP Timeout 4:00:00 + Last input 0:00:05, output 0:00:05, output hang never + Last clearing of "show interface" counters never + Output queue 0/40, 0 drops; input queue 0/75, 362 drops + 5 minute input rate 8000 bits/sec, 1 packets/sec + 5 minute output rate 11000 bits/sec, 0 packets/sec + 4615550 packets input, 1942263093 bytes, 33 no buffer + Received 1947224 broadcasts, 0 runts, 8 giants + 30351 input errors, 30351 CRC, 15187 frame, 0 overrun, 0 ignored, 0 abort + 0 watchdog, 27381 multicast + 0 input packets with dribble condition detected + 11521698 packets output, 1811832796 bytes, 0 underruns + 0 output errors, 0 collisions, 2 interface resets, 0 restarts + 0 babbles, 0 late collision, 0 deferred + 0 lost carrier, 0 no carrier + 0 output buffer failures, 0 output buffers swapped out + +Serial1/0 is up, line protocol is up + Hardware is M4T + Description: frame-relay using subinterfaces + MTU 1500 bytes, BW 64 Kbit, DLY 20000 usec, rely 255/255, load 11/255 + Encapsulation FRAME-RELAY, loopback not set, keepalive set (10 sec) + LMI enq sent 10084, LMI stat recvd 9997, LMI upd recvd 0, DTE LMI up + LMI enq recvd 0, LMI stat sent 0, LMI upd sent 0 + LMI DLCI 0 LMI type is ANSI Annex D frame relay DTE + FR SVC disabled, LAPF state down + Broadcast queue 0/200, broadcasts sent/dropped 53152/0, interface broadcasts 49826 + Last input 00:00:01, output 00:00:00, output hang never + Last clearing of "show interface" counters never + Queueing strategy: fifo + Output queue 0/8, 0 drops; input queue 0/75, 0 drops + 5 minute input rate 3000 bits/sec, 4 packets/sec + 5 minute output rate 3000 bits/sec, 4 packets/sec + 429076 packets input, 132623595 bytes, 0 no buffer + Received 0 broadcasts, 0 runts, 1 giants, 0 throttles + 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort + 474808 packets output, 73186481 bytes, 0 underruns + 0 output errors, 0 collisions, 31 interface resets + 0 output buffer failures, 0 output buffers swapped out + 35 carrier transitions DCD=up DSR=up DTR=up RTS=up CTS=up + +Serial1/0 is up, line protocol is up + Hardware is M4T + Description: frame-relay using subinterfaces + MTU 1500 bytes, BW 64 Kbit, DLY 20000 usec, rely 255/255, load 31/255 + Encapsulation FRAME-RELAY, loopback not set, keepalive set (10 sec) + LMI enq sent 39756, LMI stat recvd 39753, LMI upd recvd 0, DTE LMI up + LMI enq recvd 0, LMI stat sent 0, LMI upd sent 0 + LMI DLCI 0 LMI type is ANSI Annex D frame relay DTE + FR SVC disabled, LAPF state down + Broadcast queue 0/200, broadcasts sent/dropped 91696/0, interface broadcasts 85069 + Last input 00:00:00, output 00:00:00, output hang never + Last clearing of "show interface" counters never + Queueing strategy: fifo + Output queue 0/8, 0 drops; input queue 0/75, 0 drops + 30 second input rate 62000 bits/sec, 9 packets/sec + 30 second output rate 8000 bits/sec, 5 packets/sec + 2238005 packets input, 630502832 bytes, 0 no buffer + Received 0 broadcasts, 0 runts, 0 giants, 0 throttles + 111 input errors, 108 CRC, 0 frame, 0 overrun, 0 ignored, 3 abort + 2274950 packets output, 230325061 bytes, 0 underruns + 0 output errors, 0 collisions, 2 interface resets + 0 output buffer failures, 0 output buffers swapped out + 3 carrier transitions DCD=up DSR=up DTR=up RTS=up CTS=up + +>show int s2/2.1 +Serial2/2.1 is up, line protocol is up + Hardware is M4T + Description: Frame Relay to Sydney (134.14.63.129) + Internet address is 134.14.63.130/30 + MTU 1500 bytes, BW 512 Kbit, DLY 20000 usec, + reliability 255/255, txload 2/255, rxload 17/255 + Encapsulation FRAME-RELAY + +CIR is BW 512 Kbit == 512*1024/10 bytes/sec (?) + +This number is hand typed, but there is no alternative for the CIR. + +For FrameRelay, utilization > 50% of CIR for alarm. + +>show frame pvc int s2/3.7 + +PVC Statistics for interface Serial2/3.7 (Frame Relay DTE) + + Active Inactive Deleted Static + Local 2 0 0 0 + Switched 0 0 0 0 + Unused 0 0 0 0 + +DLCI = 17, DLCI USAGE = LOCAL, PVC STATUS = ACTIVE, INTERFACE = Serial2/3.7 + + input pkts 1287959 output pkts 1446754 in bytes 811690634 + out bytes 187572881 dropped pkts 0 in FECN pkts 2333 + in BECN pkts 0 out FECN pkts 0 out BECN pkts 0 + in DE pkts 0 out DE pkts 0 + out bcast pkts 43801 out bcast bytes 13172828 + pvc create time 8w3d, last time pvc status changed 1w2d + +>show int Vlan256 +Vlan256 is up, line protocol is up + Hardware is Cat6k RP Virtual Ethernet, address is 0030.b630.b318 (bia 0030.b630.b318) + Description: Uplink to CTC tipctc2 (Area 2) + Internet address is 192.132.156.72/26 + MTU 1500 bytes, BW 10000 Kbit, DLY 1000 usec, + reliablility 255/255, txload 1/255, rxload 44/255 + Encapsulation ARPA, loopback not set + ARP type: ARPA, ARP Timeout 04:00:00 + Last input 00:00:00, output never, output hang never + Last clearing of "show interface" counters never + Queueing strategy: fifo + Output queue 0/40, 2 drops; input queue 0/75, 1027 drops + 5 minute input rate 1754000 bits/sec, 158 packets/sec + 5 minute output rate 9000 bits/sec, 6 packets/sec + 3087365859 packets input, 816292345 bytes, 224 no buffer + Received 3085491728 broadcasts, 0 runts, 0 giants, 0 throttles + 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored + 110141390 packets output, 3736073981 bytes, 0 underruns + 0 output errors, 3 interface resets + 0 output buffer failures, 0 output buffers swapped out + +>show int e1/0 +Ethernet1/0 is up, line protocol is up + Hardware is AmdP2, address is 0050.7343.f011 (bia 0050.7343.f011) + Description: >>> Connecion to melbourne pix-e1 <<< + Internet address is 134.14.71.241/30 + MTU 1500 bytes, BW 10000 Kbit, DLY 1000 usec, + reliability 255/255, txload 3/255, rxload 1/255 + Encapsulation ARPA, loopback not set + Keepalive set (10 sec) + ARP type: ARPA, ARP Timeout 04:00:00 + Last input 00:09:05, output 00:00:00, output hang never + Last clearing of "show interface" counters never + Queueing strategy: fifo + Output queue 0/40, 0 drops; input queue 0/75, 0 drops + 30 second input rate 26000 bits/sec, 4 packets/sec + 30 second output rate 118000 bits/sec, 97 packets/sec + 3657465 packets input, 2161281566 bytes, 0 no buffer + Received 79047 broadcasts, 0 runts, 0 giants, 0 throttles + 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored + 0 input packets with dribble condition detected + 13202080 packets output, 4162655553 bytes, 0 underruns + 6 output errors, 249819 collisions, 11 interface resets + 0 babbles, 0 late collision, 207020 deferred + 6 lost carrier, 0 no carrier + 0 output buffer failures, 0 output buffers swapped out + diff --git a/src/pmdas/cisco/Tested b/src/pmdas/cisco/Tested new file mode 100644 index 0000000..fc3d6aa --- /dev/null +++ b/src/pmdas/cisco/Tested @@ -0,0 +1,76 @@ + +Cisco Model : MGS +Interface(s) e2 + +Cisco Model: Cisco 7000 +Interface(s) e1/2 f2/0 + +Cisco Model: Cisco 7000 +Interface(s) e0/0 e0/1 e0/2 e0/3 e0/4 e0/5 + f2/0 f4/0 + +Cisco Model : 4500 +Interface(s) e0 f1 + +Cisco Model : 2500 (68030) processor (revision A) 3000 Software +Interface(s) e0 s0 + +Cisco Model : 4000M +Interface(s) e0#starchild + e1 -P starchild + f0 -P starchild + s0 -P starchild + s1#starchild + s2#starchild + +Cisco Model : 4500 +Interface(s) e0 e1 + f0 f1 + + +Cisco Model : 7513 +Interface(s) e0/0 e0/1 e0/2 e0/3 e0/4 e0/5 + E1/0 E1/1 + s2/0 s2/1 s2/2 s2/3 + h3/0 + f5/0 f8/0 f9/0 f10/0 f11/0 + a12/0 + +Cisco Model : 7000 +Interface(s) e0/0 e0/1 e0/2 e0/3 e0/4 e0/5 + e1/0 e1/1 e1/2 e1/3 e1/4 e1/5 + e2/0 e2/1 e2/2 e2/3 e2/4 e2/5 + f3/0 + a4/0 + +Cisco Model : 2500 +Interfaces e0?cisco e1 s0 s1 + +Cisco Model : 3640 IOS 3600 Version 11.3(6), RELEASE SOFTWARE (fc1) +Interface(s) e0/0 s1/0 s1/1 s1/2 s1/3 + + +Cisco Model: Cat6k-MSFC +Interfaces Vlan32 Vlan33 Vlan34 Vlan35 Vlan36 Vlan37 + Vlan38 Vlan39 Vlan41 Vlan43 Vlan44 Vlan47 + Vlan48 Vlan50 Vlan51 + Vlan140 Vlan149 Vlan156 Vlan240 Vlan249 + Vlan256 + +Cisco Model: Catalyst 6509 +Interfaces Vl2 Vl3 Vl4 Vl5 Vl6 Vl7 Vl8 Vl14 Vl15 + Vl16 Vl17 Vl18 Vl19 Vl20 Vl21 Vl22 Vl23 + Vl101 Vl109 Vl110 Vl111 Vl112 Vl113 + Vl156 Vl209 Vl210 Vl211 Vl212 Vl213 + Vl256 + +Cisco Model: C2960 IOS Version 12.2(25)SEE2, RELEASE SOFTWARE (fc1) +Interfaces + Vl1 Vl50 + E0/1 E0/2 E0/3 E0/4 E0/5 E0/6 E0/7 E0/8 E0/9 E0/10 + E0/11 E0/12 E0/13 E0/14 E0/15 E0/16 E0/17 E0/18 E0/19 E0/20 + E0/21 E0/22 E0/23 E0/24 E0/25 E0/26 E0/27 E0/28 E0/29 E0/30 + E0/31 E0/32 E0/33 E0/34 E0/35 E0/36 E0/37 E0/38 E0/39 E0/40 + E0/41 E0/42 E0/43 E0/44 E0/45 E0/46 E0/47 E0/48 + G0/1 G0/2 + diff --git a/src/pmdas/cisco/cisco.c b/src/pmdas/cisco/cisco.c new file mode 100644 index 0000000..f303480 --- /dev/null +++ b/src/pmdas/cisco/cisco.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 1995-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "cisco.h" +#if defined(HAVE_SYS_RESOURCE_H) +#include +#endif +#if defined(HAVE_SYS_WAIT_H) +#include +#endif +#if defined(HAVE_PTHREAD_H) +#include +#endif +#if defined(HAVE_PRCTL_H) +#include +#endif + +extern int refreshdelay; + +#ifdef HAVE_SPROC +static pid_t sproc_pid = 0; +#elif defined (HAVE_PTHREAD_H) +#include +static pthread_t sproc_pid; +#else +#error "Need sproc or pthreads here!" +#endif + +/* + * all metrics supported in this PMD - one table entry for each + */ +static pmdaMetric metrictab[] = { + /* 0,0 ... for direct map, sigh */ + { NULL, { PMDA_PMID(0,0), 0, 0, 0, PMDA_PMUNITS(0,0,0,0,0,0) } }, + /* bytes-in */ + { NULL, { PMDA_PMID(0,1), PM_TYPE_U64, CISCO_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + /* bytes-out */ + { NULL, { PMDA_PMID(0,2), PM_TYPE_U64, CISCO_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + /* rate-in */ + { NULL, { PMDA_PMID(0,3), PM_TYPE_U32, CISCO_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) } }, + /* rate-out */ + { NULL, { PMDA_PMID(0,4), PM_TYPE_U32, CISCO_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) } }, + /* bandwidth */ + { NULL, { PMDA_PMID(0,5), PM_TYPE_U32, CISCO_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) } }, + /* bytes_out_bcast */ + { NULL, { PMDA_PMID(0,6), PM_TYPE_U64, CISCO_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + }; + +/* filled in from command line args in main() ... */ +pmdaIndom indomtab[] = { + { 0, 0, 0 }, +}; + +#ifdef HAVE_SPROC +static RETSIGTYPE +onhup(int s) +{ + signal(SIGHUP, onhup); + exit(0); +} +#endif + +/* + * the sproc starts here to refresh the metric values periodically + */ +void +refresh(void *dummy) +{ + int i; + +#ifdef HAVE_SPROC +#if HAVE_PRCTL + signal(SIGHUP, onhup); +#if HAVE_PR_TERMCHILD + prctl(PR_TERMCHILD); /* SIGHUP when the parent dies */ +#elif HAVE_PR_SET_PDEATHSIG + prctl(PR_SET_PDEATHSIG, SIGHUP); +#endif +#endif +#endif + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Starting sproc ...\n"); + for (i = 0; i < n_cisco; i++) { + int j; + + fprintf(stderr, "cisco[%d] host: %s username: %s passwd: %s prompt: %s intf:", + i, cisco[i].host, cisco[i].username, cisco[i].passwd, cisco[i].prompt); + + for (j = 0; j < n_intf; j++) { + if (intf[j].cp == (cisco+i)) + fprintf(stderr, " %d-%s", j, intf[j].interface); + } + fputc('\n', stderr); + } + } +#endif + + for ( ; ; ) { + for (i = 0; i < n_intf; i++) { + if (grab_cisco(intf+i) != -1) { + intf[i].fetched = 1; + } + else + intf[i].fetched = 0; + } + + if (parse_only) + exit(0); + + for (i = 0; i < n_cisco; i++) { + if (cisco[i].fout != NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "... %s voluntary disconnect fout=%d\n", cisco[i].host, fileno(cisco[i].fout)); +#endif + /* close CISCO telnet session */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: exit\n"); + } +#endif + fprintf(cisco[i].fout, "exit\n"); + fclose(cisco[i].fout); + cisco[i].fout = NULL; + } + if (cisco[i].fin != NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "... %s close fin=%d\n", cisco[i].host, fileno(cisco[i].fin)); +#endif + fclose(cisco[i].fin); + cisco[i].fin = NULL; + } + } + + sleep(refreshdelay); + } +} + +static int +cisco_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *avp) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + +#ifndef HAVE_SPROC + /* Check is refresh thread is still with us */ + int err; + + if ( (err = pthread_kill (sproc_pid, 0)) != 0 ) { + exit (1); + } +#endif + + if (!intf[inst].fetched) + return PM_ERR_AGAIN; + + switch (idp->item) { + + case 1: /* bytes_in */ + if (intf[inst].bytes_in == -1) return 0; + avp->ull = intf[inst].bytes_in; + break; + + case 2: /* bytes_out */ + if (intf[inst].bytes_out == -1) return 0; + avp->ull = intf[inst].bytes_out; + break; + + case 3: /* rate_in */ + if (intf[inst].rate_in == -1) return 0; + avp->ul = intf[inst].rate_in; + break; + + case 4: /* rate_out */ + if (intf[inst].rate_out == -1) return 0; + avp->ul = intf[inst].rate_out; + break; + + case 5: /* bandwidth */ + if (intf[inst].bandwidth == -1) return 0; + avp->ul = intf[inst].bandwidth; + break; + + case 6: /* bytes_out_bcast */ + if (intf[inst].bytes_out_bcast == -1) return 0; + avp->ull = intf[inst].bytes_out_bcast; + break; + + default: + return PM_ERR_PMID; + } + + return 1; +} + +void +cisco_init(pmdaInterface *dp) +{ + int i; + + pmdaSetFetchCallBack(dp, cisco_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); + + for (i = 0; i < n_intf; i++) + intf[i].fetched = 0; + + /* start the sproc for async fetches */ +#ifdef HAVE_SPROC + i = sproc_pid = sproc(refresh, PR_SADDR); +#elif defined (HAVE_PTHREAD_H) + i = pthread_create(&sproc_pid, NULL, (void (*))refresh, NULL); +#else +#error "Need sproc or pthread here!" +#endif + + if (i < 0) + dp->status = i; + else + dp->status = 0; +} + +void +cisco_done(void) +{ + int i; + + if (sproc_pid > 0) { +#ifndef HAVE_SPROC + pthread_kill(sproc_pid, SIGHUP); +#else + kill(sproc_pid, SIGHUP); +#endif + while (wait(&i) >= 0) + ; + } +} + diff --git a/src/pmdas/cisco/cisco.h b/src/pmdas/cisco/cisco.h new file mode 100644 index 0000000..acf14bc --- /dev/null +++ b/src/pmdas/cisco/cisco.h @@ -0,0 +1,86 @@ +/* + * Instance Domain Data Structures, suitable for a PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _CISCO_H +#define _CISCO_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" + +typedef struct { + char *host; /* CISCO hostname */ + __pmHostEnt *hostinfo; /* Address info for 'host' */ + int port; /* port */ + char *username; /* username */ + char *passwd; /* password */ + char *prompt; /* command prompt */ + FILE *fout; /* write cmds here */ + FILE *fin; /* read output here */ +} cisco_t; + +typedef struct { + + cisco_t *cp; /* which CISCO? */ + char *interface; /* interface name, e.g. s0 or e10/10 */ + int fetched; /* valid stats? */ + __uint32_t bandwidth; /* peak bandwidth */ + __uint32_t rate_in; + __uint32_t rate_out; + __uint64_t bytes_in; /* stats */ + __uint64_t bytes_out; + __uint64_t bytes_out_bcast; +} intf_t; + +extern cisco_t *cisco; +extern int n_cisco; +extern intf_t *intf; +extern int n_intf; + +/* + * Supported Cisco Interfaces + */ +typedef struct { + char *type; /* NULL to skip, else unique per interface + * type */ + char *name; /* full name as per "show" */ +} intf_tab_t; + +extern intf_tab_t intf_tab[]; +extern int num_intf_tab; + +#define CISCO_INDOM 0 +extern pmdaIndom indomtab[]; +extern pmdaInstid *_router; + +#define PWPROMPT "Password:" +#define USERPROMPT "Username:" + +extern int conn_cisco(cisco_t *); +extern int grab_cisco(intf_t *); +extern int dousername(cisco_t *, char **); +extern int dopasswd(cisco_t *, char *); +extern char *mygetwd(FILE *, char *); + +extern int parse_only; + +#endif /* _CISCO_H */ diff --git a/src/pmdas/cisco/cisco.in_util.pmie b/src/pmdas/cisco/cisco.in_util.pmie new file mode 100644 index 0000000..75ecd08 --- /dev/null +++ b/src/pmdas/cisco/cisco.in_util.pmie @@ -0,0 +1,64 @@ +#pmieconf-rules 1 +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) + +rule cisco.in_util + summary = "$rule$" + enumerate = hosts + predicate = +"some_inst ( + 100 * cisco.rate_in $hosts$ / cisco.bandwidth $hosts$ + > $threshold$ +)" + enabled = no + version = 1 + help = +"Some Cisco router interface exceeded threshold percent of its +peak bandwidth receiving data during the last sample interval. +Use the command: + $ pminfo -f cisco.bandwidth +to discover the list of Cisco router interfaces currently being +monitored by the Cisco PMDA - pmdacisco(1)."; + +string rule + default = "Cisco router inbound bandwidth saturation" + modify = no + display = no; + +percent threshold + default = 90 + help = +"Threshold percentage for Cisco router saturation, in the range 0 +(idle) to 100 (operating at peak bandwidth)"; + +string action_expand + default = %v%util[%i]@%h + display = no + modify = no; + +string email_expand + default = "host: %h Cisco router: %i inbound utilization: %v%" + display = no + modify = no; + + +# Configuration info specific to non-PCP tools follows... +# + +# for SGI Embedded Support Partner integration: +string esp_type + default = "0x200093" + display = no + modify = no; + +# for EnlightenDSM integration: +string enln_test + default = cisco.in_util + display = no + modify = no; +string enln_units + default = %util[%i] + display = no + modify = no; + +# +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) diff --git a/src/pmdas/cisco/cisco.out_util.pmie b/src/pmdas/cisco/cisco.out_util.pmie new file mode 100644 index 0000000..e003f59 --- /dev/null +++ b/src/pmdas/cisco/cisco.out_util.pmie @@ -0,0 +1,64 @@ +#pmieconf-rules 1 +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) + +rule cisco.out_util + summary = "$rule$" + enumerate = hosts + predicate = +"some_inst ( + 100 * cisco.rate_out $hosts$ / cisco.bandwidth $hosts$ + > $threshold$ +)" + enabled = no + version = 1 + help = +"Some Cisco router interface exceeded threshold percent of its +peak bandwidth sending data during the last sample interval. +Use the command: + $ pminfo -f cisco.bandwidth +to discover the list of Cisco router interfaces currently being +monitored by the Cisco PMDA - pmdacisco(1)."; + +string rule + default = "Cisco router outbound bandwidth saturation" + modify = no + display = no; + +percent threshold + default = 90 + help = +"Threshold percentage for Cisco router saturation, in the range 0 +(idle) to 100 (operating at peak bandwidth)"; + +string action_expand + default = %v%util[%i]@%h + display = no + modify = no; + +string email_expand + default = "host: %h Cisco router: %i outbound utilization: %v%" + display = no + modify = no; + + +# Configuration info specific to non-PCP tools follows... +# + +# for SGI Embedded Support Partner integration: +string esp_type + default = "0x200094" + display = no + modify = no; + +# for EnlightenDSM integration: +string enln_test + default = cisco.out_util + display = no + modify = no; +string enln_units + default = %util[%i] + display = no + modify = no; + +# +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) diff --git a/src/pmdas/cisco/help b/src/pmdas/cisco/help new file mode 100644 index 0000000..eeb8c5f --- /dev/null +++ b/src/pmdas/cisco/help @@ -0,0 +1,76 @@ +# +# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# cisco PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric-name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric-name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ CISCO.1 Interfaces on Cisco router +There is one instance in this domain for each interface on a Cisco router +that the Cisco PMDA (Performance Metrics Domain Agent) has been told about +when the PMDA is started. + +The names of the instances are of the form hostname:tX where "t" is one of +"a" for ATM, "B" for ISDN BRI, "e" for Ethernet, "E" (FastEthernet), "f" for +Fddi, "h" for HSSC, "s" for Serial or "Vl" for Vlan. The "X" is the +interface identifier which is either an integer (e.g. 4000 Series routers) or +two integers separated by a slash (e.g. 7000 Series routers) or three +integers separated by a slash and a period (Frame-Relay PVCs on serial line +subinterfaces). + +@ cisco.bytes_in Total Kbytes input to the Cisco +Total number of Kbytes input to the Cisco on this interface. + +Note that due to network delays in extracting the metrics from the +Cisco routers, any rate computed from this metric over small deltas in time +are likely to be subject to wide variance. + +@ cisco.bytes_out Total Kbytes output from the Cisco +Total number of Kbytes output from the Cisco on this interface. + +Note that due to network delays in extracting the metrics from the +Cisco routers, any rate computed from this metric over small deltas in time +are likely to be subject to wide variance. + +@ cisco.bytes_out_bcast Total broadcast Kbytes output from the Cisco +Total number of broadcast Kbytes output from the Cisco on this interface. + +Note that due to network delays in extracting the metrics from the +Cisco routers, any rate computed from this metric over small deltas in +time are likely to be subject to wide variance. + +@ cisco.rate_in 5 minutes average input rate in bytes (not bits!) per second +Cisco's computed average input rate in bytes per second, over the recent +past, for this interface. + +@ cisco.rate_out 5 minutes average output rate in bytes (not bits!) per second +Cisco's computed average output rate in bytes per second, over the recent +past, for this interface. + +@ cisco.bandwidth peak interface bandwidth in bytes per second diff --git a/src/pmdas/cisco/interface.c b/src/pmdas/cisco/interface.c new file mode 100644 index 0000000..022c8ac --- /dev/null +++ b/src/pmdas/cisco/interface.c @@ -0,0 +1,46 @@ +/* + * Known Cisco Interfaces + * + * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "./cisco.h" + +/* + * first letter defines command line argument for interface type + * (0 means not allowed in command line) + * full name of interface is as found in "show interface" command + * output. + */ + +intf_tab_t intf_tab[] = { + { "s", "Serial" }, + { "e", "Ethernet" }, + { "E", "FastEthernet" }, + { "f", "Fddi" }, + { "h", "Hssi" }, + { "a", "ATM" }, + { "B", "BRI" }, + { "Vl", "Vlan" }, + { "G", "GigabitEthernet" }, + { NULL, "Controller" }, + { NULL, "Port-channel" }, + { NULL, "Dialer" }, + { NULL, "Loopback" }, +}; + +int num_intf_tab = sizeof(intf_tab) / sizeof(intf_tab[0]); diff --git a/src/pmdas/cisco/parse.sh b/src/pmdas/cisco/parse.sh new file mode 100755 index 0000000..c35a27c --- /dev/null +++ b/src/pmdas/cisco/parse.sh @@ -0,0 +1,3 @@ +#!/bin/sh +. $PCP_DIR/etc/pcp.env +exec $PCP_PMDAS_DIR/cisco/pmdacisco -n parse -C $@ diff --git a/src/pmdas/cisco/pmda.c b/src/pmdas/cisco/pmda.c new file mode 100644 index 0000000..d78190c --- /dev/null +++ b/src/pmdas/cisco/pmda.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2012,2014 Red Hat. + * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Cisco PMDA, based on generic driver for a daemon-based PMDA + * - cisco interfaces to monitor are named in the command line as + * hostname:tX[@username] + * hostname:tX[?passwd] + * hostname:tX[@username?passwd] + * hostname:tX[!prompt] + * hostname:tX[@username!prompt] + * hostname:tX[?passwd!prompt] + * hostname:tX[@username?passwd!prompt] + * + * where t identifies an interface type as defined by intf_tab[] in + * interface.c + * and X is either the ordinal i/f number (base 0, for Series 4000) + * or the card/port number (Series 7000 routers) + * e.g sydcisco.sydney:s0 (Frame-Relay to Mtn View) + * sydcisco.sydney:s1 (ISDN to Melbourne) + * cisco.melbourne:s0 (ISDN to Sydney) + * b8u-cisco1-e15.corp:e0 (Ethernet in Bldg 8 upper) + * b9u-cisco1-81.engr.sgi.com:f2/0 + * wanbris.brisbane:B0 (BRI ISDN) + * If specified the username (after the @ delimiter) and/or the + * user-level password (after the ? delimiter) and/or the prompt (after + * the ! delimiter) over-rides the global username and/or user-level + * password and/or prompt, as specified via -U and/or -P and/or -s + * options and applies for all occurrences of the hostname. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "./cisco.h" + +pmdaInstid *_router; +cisco_t *cisco; +int n_cisco; +intf_t *intf; +int n_intf; +int refreshdelay = 120; /* default poll every two minutes */ +char *pmdausername; /* username for the pmda */ +char *username; /* username */ +char *passwd; /* user-level password */ +char *prompt = ">"; /* command prompt */ +int port = 23; +int parse_only; +int no_lookups; + +extern void cisco_init(pmdaInterface *); +extern void cisco_done(void); + +int +main(int argc, char **argv) +{ + int err = 0; + int sep = __pmPathSeparator(); + char *endnum; + pmdaInterface dispatch; + int n; + int i; + int c; + char helptext[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&pmdausername); + + snprintf(helptext, sizeof(helptext), "%s%c" "cisco" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, CISCO, + "cisco.log", helptext); + + while ((c = pmdaGetOpt(argc, argv, "D:d:h:i:l:pu:6:" "CM:Nn:P:r:s:U:x:?", + &dispatch, &err)) != EOF) { + switch (c) { + + case 'C': /* parser checking mode (debugging) */ + pmDebug = DBG_TRACE_APPL0; + parse_only++; + break; + + case 'N': /* do not perform name lookups (debugging) */ + no_lookups = 1; + break; + + case 'n': /* set program name, for parse (debugging) */ + pmProgname = optarg; + break; + + case 'P': /* passwd */ + passwd = optarg; + break; + + case 'r': + refreshdelay = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, "%s: -r requires numeric (number of seconds) argument\n", + pmProgname); + err++; + } + break; + + case 's': /* command prompt */ + prompt = optarg; + break; + + case 'M': /* username (for the PMDA) */ + pmdausername = optarg; + break; + + case 'U': /* username (for the Cisco) */ + username = optarg; + break; + + case 'x': + port = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, "%s: -x requires numeric argument\n", + pmProgname); + err++; + } + break; + + case '?': + err++; + } + } + + n_intf = argc - optind; + if (n_intf == 0 || err) { + fprintf(stderr, + "Usage: %s [options] host:{a|B|E|e|f|h|s}N[/M[.I]] [...]\n\n", + pmProgname); + fputs("Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -i port expect PMCD to connect on given inet port (number or name)\n" + " -l logfile redirect diagnostics and trace output to logfile\n" + " -M username user account to run PMDA under (default \"pcp\")\n" + " -p expect PMCD to supply stdin/stdout (pipe)\n" + " -P password default user-level Cisco password\n" + " -r refresh update metrics every refresh seconds\n" + " -s prompt Cisco command prompt [default >]\n" + " -u socket expect PMCD to connect on given unix domain socket\n" + " -U username Cisco username\n" + " -x port telnet port [default 23]\n" + " -6 port expect PMCD to connect on given ipv6 port (number or name)\n", + stderr); + exit(1); + } + + /* force errors from here on into the log */ + if (!parse_only) { + pmdaOpenLog(&dispatch); + __pmSetProcessIdentity(pmdausername); + } else { + dispatch.version.two.text = NULL; + dispatch.version.two.ext->e_helptext = NULL; + } + + /* + * build the instance domain and cisco data structures from the + * command line arguments. + */ + if ((_router = (pmdaInstid *)malloc(n_intf * sizeof(pmdaInstid))) == NULL) { + __pmNoMem("main.router", n_intf * sizeof(pmdaInstid), PM_FATAL_ERR); + } + if ((intf = (intf_t *)malloc(n_intf * sizeof(intf_t))) == NULL) { + __pmNoMem("main.intf", n_intf * sizeof(intf_t), PM_FATAL_ERR); + } + /* pre-allocated cisco[] to avoid realloc and ptr movement */ + if ((cisco = (cisco_t *)malloc(n_intf * sizeof(cisco_t))) == NULL) { + __pmNoMem("main.cisco", n_intf * sizeof(cisco_t), PM_FATAL_ERR); + } + + indomtab[CISCO_INDOM].it_numinst = n_intf; + indomtab[CISCO_INDOM].it_set = _router; + + for (n = 0 ; optind < argc; optind++, n++) { + char *p = strdup(argv[optind]); + char *q; + char *myusername; + char *mypasswd; + char *myprompt; + + myprompt = strchr(p, '!'); + if (myprompt) { + /* save prompt for later */ + *myprompt++ = '\0'; + } + else + myprompt = NULL; + mypasswd = strchr(p, '?'); + if (mypasswd) { + /* save user-level password for later */ + *mypasswd++ = '\0'; + } + else + mypasswd = passwd; + myusername = strchr(p, '@'); + if (myusername) { + /* save username for later */ + *myusername++ = '\0'; + } + else + myusername = username; + + _router[n].i_inst = n; + _router[n].i_name = strdup(p); + + if ((q = strchr(p, ':')) == NULL) + goto badintfspec; + *q++ = '\0'; + for (i = 0; i < num_intf_tab; i++) { + if (strncmp(q, intf_tab[i].type, strlen(intf_tab[i].type)) == 0) + break; + } + if (i == num_intf_tab) + goto badintfspec; + if (strcmp(intf_tab[i].type, "E") == 0) { + /* + * Cisco parser is case insensitive, so 'E' means "Ethernet" + * and 'F' means "Fddi", need to use "FastEthernet" here + */ + q++; + intf[n].interface = (char *)malloc(strlen("FastEthernet")+strlen(q)+1); + if ((intf[n].interface = (char *)malloc(strlen("FastEthernet")+strlen(q)+1)) == NULL) { + __pmNoMem("main.cisco", strlen("FastEthernet")+strlen(q)+1, PM_FATAL_ERR); + } + strcpy(intf[n].interface, "FastEthernet"); + strcat(intf[n].interface, q); + } + else + intf[n].interface = q; + + for (i = 0; i < n_cisco; i++) { + if (strcmp(p, cisco[i].host) == 0) + break; + } + if (i == n_cisco) { + __pmHostEnt *hostInfo = NULL; + + if (!no_lookups) + hostInfo = __pmGetAddrInfo(p); + + if (!hostInfo && parse_only) { + FILE *f; + + /* + * for debugging, "host" may be a file ... + */ + if ((f = fopen(p, "r")) == NULL) { + fprintf(stderr, "%s: unknown hostname or filename %s: %s\n", + pmProgname, argv[optind], hoststrerror()); + /* abandon this host (cisco) */ + continue; + } + else { + fprintf(stderr, "%s: assuming file %s contains output from \"show int\" command\n", + pmProgname, p); + + cisco[i].host = p; + cisco[i].username = myusername != NULL ? myusername : username; + cisco[i].passwd = mypasswd != NULL ? mypasswd : passwd; + cisco[i].prompt = myprompt != NULL ? myprompt : prompt; + cisco[i].fin = f; + cisco[i].fout = stdout; + n_cisco++; + } + } else if (!hostInfo) { + fprintf(stderr, "%s: unknown hostname %s: %s\n", + pmProgname, p, hoststrerror()); + /* abandon this host (cisco) */ + continue; + } else { + cisco[i].host = p; + cisco[i].username = myusername != NULL ? myusername : username; + cisco[i].passwd = mypasswd != NULL ? mypasswd : passwd; + cisco[i].prompt = myprompt != NULL ? myprompt : prompt; + cisco[i].fin = NULL; + cisco[i].fout = NULL; + cisco[i].hostinfo = hostInfo; + cisco[i].port = port; + + n_cisco++; + fprintf(stderr, "Adding new host %s\n", p); + fflush(stderr); + } + } + else { + if (cisco[i].username == NULL) { + if (myusername != NULL) + /* username on 2nd or later interface ... applies to all */ + cisco[i].username = myusername; + } + else { + if (myusername != NULL) { + if (strcmp(cisco[i].username, myusername) != 0) { + fprintf(stderr, + "%s: conflicting usernames (\"%s\" " + "and \"%s\") for cisco \"%s\"\n", + pmProgname, cisco[i].username, myusername, + cisco[i].host); + exit(1); + } + } + } + if (cisco[i].passwd == NULL) { + if (mypasswd != NULL) + /* passwd on 2nd or later interface ... applies to all */ + cisco[i].passwd = mypasswd; + } + else { + if (mypasswd != NULL) { + if (strcmp(cisco[i].passwd, mypasswd) != 0) { + fprintf(stderr, + "%s: conflicting user-level passwords (\"%s\" " + "and \"%s\") for cisco \"%s\"\n", + pmProgname, cisco[i].passwd, mypasswd, + cisco[i].host); + exit(1); + } + } + } + if (cisco[i].prompt == NULL) { + if (myprompt != NULL) + /* prompt on 2nd or later interface ... applies to all */ + cisco[i].prompt = myprompt; + } + else { + if (myprompt != NULL) { + if (strcmp(cisco[i].prompt, myprompt) != 0) { + fprintf(stderr, + "%s: conflicting user-level prompts (\"%s\" " + "and \"%s\") for cisco \"%s\"\n", + pmProgname, cisco[i].prompt, myprompt, + cisco[i].host); + exit(1); + } + } + } + } + + intf[n].cp = cisco+i; + /* + * special one-trip initialization for Frame-Relay over serial + * lines ... see grab_cisco() + */ + intf[n].bandwidth = -2; + + fprintf(stderr, "Interface %s(%d) is on host %s\n", + intf[n].interface, n, cisco[i].host); + fflush(stderr); + + continue; + +badintfspec: + fprintf(stderr, "%s: bad interface specification \"%s\"\n", pmProgname, argv[optind]); + fprintf(stderr, " should be like sydcisco.sydney:s1 or b9u-cisco1-81.engr.sgi.com:f2/0\n"); + fprintf(stderr, " or cisco.melbourne:e0?secret\n"); + exit(1); + } + + if (n_cisco == 0) { + fprintf(stderr, "%s: Nothing to monitor\n", pmProgname); + exit(1); + } + + if (parse_only) { + fprintf(stderr, "Sleeping while sproc does the work ... SIGINT to terminate\n"); + cisco_init(&dispatch); + for (i = 0; i < n_intf; i++) + intf[i].fetched = 0; + pause(); + } else { + /* set up connection to PMCD */ + cisco_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + } + + cisco_done(); + exit(0); +} diff --git a/src/pmdas/cisco/pmns b/src/pmdas/cisco/pmns new file mode 100644 index 0000000..fc1ce50 --- /dev/null +++ b/src/pmdas/cisco/pmns @@ -0,0 +1,28 @@ +/* + * Metrics for cisco PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +cisco { + bandwidth CISCO:0:5 + bytes_in CISCO:0:1 + bytes_out CISCO:0:2 + bytes_out_bcast CISCO:0:6 + rate_in CISCO:0:3 + rate_out CISCO:0:4 +} diff --git a/src/pmdas/cisco/probe.c b/src/pmdas/cisco/probe.c new file mode 100644 index 0000000..5cd5b4d --- /dev/null +++ b/src/pmdas/cisco/probe.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2012 Red Hat. + * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "./cisco.h" + +int port = 23; +int seen_fr = 0; +char *prompt = ">"; /* unique suffix to IOS prompt */ + +char * +mygetfirstwd(FILE *f) +{ + char *p; + int c; + char line[1024]; + char *lp; + + for ( ; ; ) { + c = fgetc(f); + if (c == EOF) + break; + if (c == '\r' || c == '\n') + continue; + if (c != ' ') { + ungetc(c, f); + break; + } + lp = line; + while ((c = fgetc(f)) != EOF) { + *lp++ = c; + if (c == '\r' || c == '\n') { + *lp = '\0'; + break; + } + } + /* + * some interesting things to look for here ... + */ + if (strncmp(&line[1], "Encapsulation FRAME-RELAY", strlen("Encapsulation FRAME-RELAY")) == 0) { + seen_fr = 1; + } + } + /* either EOF, or line starts with a non-space */ + + p = mygetwd(f, prompt); + + if (p != NULL && (strlen(p) < strlen(prompt) || + strcmp(&p[strlen(p)-strlen(prompt)], prompt)) != 0) { + /* skip to end of line, ready for next one */ + while ((c = fgetc(f)) != EOF) { + if (c == '\r' || c == '\n') + break; + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "mygetfirstwd: %s\n", p == NULL ? "" : p); +#endif + + return p; +} + +#define PREAMBLE 1 +#define IN_BODY 2 + +static void +probe_cisco(cisco_t * cp) +{ + char *w; + int fd; + int fd2; + int first = 1; + char *pass = NULL; + int defer = 0; + int state = PREAMBLE; + int i; + int namelen; + char *ctype = NULL; + char *name = NULL; + + if (cp->fin == NULL) { + fd = conn_cisco(cp); + if (fd == -1) { + fprintf(stderr, "grab_cisco(%s): connect failed: %s\n", + cp->host, osstrerror()); + return; + } + else { + cp->fin = fdopen (fd, "r"); + if ((fd2 = dup(fd)) < 0) { + perror("dup"); + exit(1); + } + cp->fout = fdopen (fd2, "w"); + if (cp->username != NULL) { + /* + * Username stuff ... + */ + if (dousername(cp, &pass) == 0) { + exit(1); + } + } + if (cp->passwd != NULL) { + /* + * User-level password stuff ... + */ + if (dopasswd(cp, pass) == 0) { + exit(1); + } + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: \n"); + fprintf(stderr, "Send: terminal length 0\n"); + } +#endif + fprintf(cp->fout, "\n"); + fflush(cp->fout); + fprintf(cp->fout, "terminal length 0\n"); + fflush(cp->fout); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: show int\n"); + } +#endif + fprintf(cp->fout, "show int\n"); + fflush(cp->fout); + + } + else { + /* + * parsing text from a file, not a TCP/IP connection to a + * Cisco device + */ + ; + } + + for ( ; ; ) { + w = mygetfirstwd(cp->fin); + if (defer && ctype != NULL && name != NULL) { + if (seen_fr) { + if (first) + first = 0; + else + putchar(' '); + printf("%s%s", ctype, name); + free(name); + name = NULL; + } + } + defer = 0; + if (w == NULL) { + /* + * End of File (telenet timeout?) + */ + fprintf(stderr, "grab_cisco(%s): forced disconnect fin=%d\n", + cp->host, fileno(cp->fin)); + return; + } + if (*w == '\0') + continue; + if (state == PREAMBLE) { + if (strcmp(w, "show") == 0) + state = IN_BODY; + else if (strcmp(w, PWPROMPT) == 0) { + fprintf(stderr, + "Error: user-level password required for \"%s\"\n", + cp->host); + exit(1); + } + continue; + } + else { + if (strlen(w) >= strlen(prompt) && + strcmp(&w[strlen(w)-strlen(prompt)], prompt) == 0) + break; + ctype = NULL; + for (i = 0; i < num_intf_tab; i++) { + namelen = strlen(intf_tab[i].name); + if (strncmp(w, intf_tab[i].name, namelen) == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "Match: if=%s word=%s\n", intf_tab[i].name, w); + } +#endif + name = strdup(&w[namelen]); + ctype = intf_tab[i].type; + if (intf_tab[i].type != NULL && strcmp(intf_tab[i].type, "a") == 0) { + /* + * skip ATMN.M ... need ATMN + */ + if (strchr(&w[namelen], '.') != NULL) + ctype = NULL; + } + else if (intf_tab[i].type != NULL && strcmp(intf_tab[i].type, "s") == 0) { + /* + * skip SerialN.M ... need SerialN, unless frame-relay + */ + if (strchr(&w[namelen], '.') != NULL) + defer = 1; + } + break; + } + } + if (i == num_intf_tab) + fprintf(stderr, "%s: Warning, unknown interface: %s\n", pmProgname, w); + if (ctype != NULL && name != NULL && !defer) { + if (first) + first = 0; + else + putchar(' '); + printf("%s%s", ctype, name); + free(name); + name = NULL; + } + } + } + putchar('\n'); + + /* close CISCO telnet session */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: exit\n"); + } +#endif + fprintf(cp->fout, "exit\n"); + fflush(cp->fout); + + return; +} + +int +main(int argc, char **argv) +{ + int errflag = 0; + int Nflag = 0; + int c; + int sts; + char *endnum; + char *passwd = NULL; + char *username = NULL; + __pmHostEnt *hostInfo = NULL; + + __pmSetProgname(argv[0]); + + while ((c = getopt(argc, argv, "ND:P:s:U:x:?")) != EOF) { + switch (c) { + + case 'N': /* check flag */ + Nflag = 1; + break; + + case 'D': /* debug flag */ + sts = __pmParseDebug(optarg); + if (sts < 0) { + fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n", + pmProgname, optarg); + errflag++; + } + else + pmDebug |= sts; + break; + + case 'P': /* passwd */ + passwd = optarg; + break; + + case 's': /* prompt */ + prompt = optarg; + break; + + case 'U': /* username */ + username = optarg; + break; + + case 'x': + port = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, "%s: -x requires numeric argument\n", + pmProgname); + errflag++; + } + break; + + case '?': + errflag++; + } + } + + if (errflag || optind != argc-1) { + fprintf(stderr, "Usage: %s [-U username] [-P passwd] [-s prompt] [-x port] host\n\n", pmProgname); + exit(1); + } + + if (!Nflag) + hostInfo = __pmGetAddrInfo(argv[optind]); + + if (hostInfo == NULL) { + FILE *f; + + if ((f = fopen(argv[optind], "r")) == NULL) { + fprintf(stderr, "%s: unknown hostname or filename %s: %s\n", + pmProgname, argv[optind], hoststrerror()); + exit(1); + } + else { + cisco_t c; + + fprintf(stderr, "%s: assuming file %s contains output from \"show int\" command\n", + pmProgname, argv[optind]); + + c.host = argv[optind]; + c.username = NULL; + c.passwd = NULL; + c.fin = f; + c.fout = fopen("/dev/null", "w"); + c.prompt = prompt; + + probe_cisco(&c); + } + } else { + cisco_t c; + + c.host = argv[optind]; + c.username = username; + c.passwd = passwd; + c.fin = NULL; + c.fout = NULL; + c.prompt = prompt; + c.hostinfo = hostInfo; + c.port = 23; /* telnet */ + + probe_cisco(&c); + } + + exit(0); +} diff --git a/src/pmdas/cisco/root b/src/pmdas/cisco/root new file mode 100644 index 0000000..e0c1b81 --- /dev/null +++ b/src/pmdas/cisco/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { cisco } + +#include "pmns" + diff --git a/src/pmdas/cisco/telnet.c b/src/pmdas/cisco/telnet.c new file mode 100644 index 0000000..bcee1db --- /dev/null +++ b/src/pmdas/cisco/telnet.c @@ -0,0 +1,755 @@ +/* + * Copyright (c) 2012 Red Hat. + * Copyright (c) 1995-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "./cisco.h" + +extern int port; + +int +conn_cisco(cisco_t * cp) +{ + __pmFdSet wfds; + __pmSockAddr *myaddr; + void *enumIx; + int flags = 0; + int fd; + int ret; + + fd = -1; + enumIx = NULL; + for (myaddr = __pmHostEntGetSockAddr(cp->hostinfo, &enumIx); + myaddr != NULL; + myaddr = __pmHostEntGetSockAddr(cp->hostinfo, &enumIx)) { + /* Create a socket */ + if (__pmSockAddrIsInet(myaddr)) + fd = __pmCreateSocket(); + else if (__pmSockAddrIsIPv6(myaddr)) + fd = __pmCreateIPv6Socket(); + else + continue; + if (fd < 0) { + __pmSockAddrFree(myaddr); + continue; /* Try the next address */ + } + + /* Attempt to connect */ + flags = __pmConnectTo(fd, myaddr, cp->port); + __pmSockAddrFree(myaddr); + + if (flags < 0) { + /* + * Mark failure in case we fall out the end of the loop + * and try next address. fd has been closed in __pmConnectTo(). + */ + setoserror(ECONNREFUSED); + fd = -1; + continue; + } + + /* FNDELAY and we're in progress - wait on select */ + __pmFD_ZERO(&wfds); + __pmFD_SET(fd, &wfds); + ret = __pmSelectWrite(fd+1, &wfds, NULL); + + /* Was the connection successful? */ + if (ret == 0) + setoserror(ETIMEDOUT); + else if (ret > 0) { + ret = __pmConnectCheckError(fd); + if (ret == 0) + break; + setoserror(ret); + } + + /* Unsuccessful connection. */ + __pmCloseSocket(fd); + fd = -1; + } /* loop over addresses */ + + if (fd == -1) { + fprintf(stderr, "conn_cisco(%s): connect: %s\n", + cp->host, netstrerror()); + return -1; + } + + fd = __pmConnectRestoreFlags(fd, flags); + if (fd < 0) { + fprintf(stderr, "conn_cisco(%s): setsockopt: %s\n", + cp->host, netstrerror()); + return -1; + } + + return fd; +} + +static void +skip2eol(FILE *f) +{ + int c; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "skip2eol:"); +#endif + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "%c", c); +#endif + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fputc('\n', stderr); +#endif +} + +char * +mygetwd(FILE *f, char *prompt) +{ + char *p; + int c; + static char buf[1024]; + int len_prompt = strlen(prompt); + int found_prompt = 0; + + p = buf; + + while ((c = fgetc(f)) != EOF) { + if (c == '\r' || c == '\n' || c == ' ' || c == '\t') { + if (p == buf) + continue; + break; + } + *p++ = c; + if (p-buf >= len_prompt && strncmp(&p[-len_prompt], prompt, len_prompt) == 0) { + found_prompt = 1; + break; + } + } + *p = '\0'; + + if (feof(f)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "mygetwd: EOF fd=%d\n", fileno(f)); +#endif + return NULL; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "mygetwd: fd=%d wd=\"%s\"%s\n", fileno(f), buf, found_prompt ? " [prompt]" : ""); +#endif + + return buf; +} + +/* + * The CISCO "show interface" command output is parsed. + * + * See the file Samples for examples. + * + * The parser is a Finite State Automaton (FSA) that follows these + * rules: + * + * SHOW_INT style ... uses "show int " command + * state token next state + * NOISE IN_REPORT + * IN_REPORT Description: skip rest of line, IN_REPORT + * IN_REPORT DONE + * IN_REPORT minute RATE + * IN_REPORT second RATE + * IN_REPORT input, BYTES_IN + * IN_REPORT output, BYTES_OUT + * IN_REPORT BW BW + * RATE input skip next token, RATE_IN + * RATE output skip next token, RATE_OUT + * RATE_IN (rate_in) IN_REPORT + * RATE_OUT (rate_out) IN_REPORT + * BYTES_IN (bytes_in) IN_REPORT + * BYTES_OUT (bytes_out) IN_REPORT + * BW (bandwidth) IN_REPORT + * + * SHOW_FRAME style ... uses "show frame pvc int " command + * state token next state + * NOISE IN_REPORT + * IN_REPORT Description: skip rest of line, IN_REPORT + * IN_REPORT DONE + * IN_REPORT 1st bytes BYTES_IN + * IN_REPORT 2nd bytes BYTES_OUT + * IN_REPORT 3rd bytes BYTES_OUT_BCAST + * BYTES_IN (bytes_in) IN_REPORT + * BYTES_OUT (bytes_out) IN_REPORT + * BYTES_OUT_BCAST (bytes_out_bcast) IN_REPORT + * + * Note lines are terminated with \r + */ + +#define DONE -1 +#define NOISE 0 +#define IN_REPORT 1 +#define RATE 2 +#define RATE_IN 3 +#define RATE_OUT 4 +#define BYTES_IN 5 +#define BYTES_OUT 6 +#define BW 7 +#define BYTES_OUT_BCAST 8 + +#ifdef PCP_DEBUG +static char *statestr[] = { + "done", "noise", "in_report", + "rate", "rate_in", "rate_out", "bytes_in", "bytes_out", "bw", + "bytes_out_bcast" +}; +#endif + +int +dousername(cisco_t *cp, char **pw_prompt) +{ + char *w; + int len, done = 0; + int len_prompt = strlen(cp->prompt); + + for ( ; ; ) { + w = mygetwd(cp->fin, cp->prompt); + if (w == NULL) + break; + if (strlen(w) >= len_prompt && strncmp(&w[strlen(w)-len_prompt], cp->prompt, len_prompt) == 0) + break; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Username:? got - %s\n", w); +#endif + if (strcmp(w, USERPROMPT) == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send username: %s\n", cp->username); + } +#endif + fprintf(cp->fout, "%s\n", cp->username); + fflush(cp->fout); + for ( ; ; ) { + w = mygetwd(cp->fin, cp->prompt); + if (w == NULL || strcmp(w, USERPROMPT) == 0) + /* closed connection or Username re-prompt */ + break; + len = strlen(w); + if ((len >= len_prompt && strncmp(&w[len-len_prompt], cp->prompt, len_prompt) == 0) || + w[len-1] == ':') { + /* command prompt or passwd */ + if (w[len-1] == ':') + *pw_prompt = w; + done = 1; + break; + } + } + break; + } + } + + if (done == 0) { + fprintf(stderr, "Error: Cisco username negotiation failed for \"%s\"\n", + cp->host); + fprintf(stderr, +"To check that a username is required, enter the following command:\n" +" $ telnet %s\n" +"If the prompt \"%s\" does not appear, no username is required.\n" +"Otherwise, enter the username \"%s\" to check that this\n" +"is correct.\n", +cp->host, USERPROMPT, cp->username); + } + + return done; +} + +int +dopasswd(cisco_t *cp, char *pw_prompt) +{ + char *w; + int done = 0; + int len_prompt = strlen(cp->prompt); + + for ( ; ; ) { + if (pw_prompt) /* dousername may have read passwd prompt */ + w = pw_prompt; + else + w = mygetwd(cp->fin, cp->prompt); + pw_prompt = NULL; + if (w == NULL) + break; + if (strlen(w) >= len_prompt && strncmp(&w[strlen(w)-len_prompt], cp->prompt, len_prompt) == 0) + break; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Password:? got - %s\n", w); +#endif + if (strcmp(w, PWPROMPT) == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send passwd: %s\n", cp->passwd); + } +#endif + fprintf(cp->fout, "%s\n", cp->passwd); + fflush(cp->fout); + for ( ; ; ) { + w = mygetwd(cp->fin, cp->prompt); + if (w == NULL || strcmp(w, PWPROMPT) == 0) + /* closed connection or user-level password re-prompt */ + break; + if (strlen(w) >= len_prompt && strncmp(&w[strlen(w)-len_prompt], cp->prompt, len_prompt) == 0) { + /* command prompt */ + done = 1; + break; + } + } + break; + } + } + + if (done == 0) { + fprintf(stderr, "Error: Cisco user-level password negotiation failed for \"%s\"\n", + cp->host); + fprintf(stderr, +"To check that a user-level password is required, enter the following command:\n" +" $ telnet %s\n" +"If the prompt \"%s\" does not appear, no user-level password is required.\n" +"Otherwise, enter the user-level password \"%s\" to check that this\n" +"is correct.\n", +cp->host, PWPROMPT, cp->passwd); + } + + return done; +} + +static int timeout; + +void +onalarm(int dummy) +{ + timeout = 1; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Alarm timeout!\n"); + } +#endif + +} + +static int +get_fr_bw(cisco_t *cp, char *interface) +{ + int state = NOISE; + int bandwidth = -1; + char *w; + int len_prompt = strlen(cp->prompt); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: s%s\n", interface); + } +#endif + fprintf(cp->fout, "show int s%s\n", interface); + fflush(cp->fout); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "BW Parse:"); +#endif + while (state != DONE) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "[%s] ", statestr[state+1]); +#endif + w = mygetwd(cp->fin, cp->prompt); + if (w == NULL || timeout) { + /* + * End of File (telenet timeout?) + */ + alarm(0); + return -1; + } + switch (state) { + + case NOISE: + if (strncmp(w, "Serial", 6) == 0 && strcmp(&w[6], interface) == 0) + state = IN_REPORT; + break; + + case IN_REPORT: + if (strcmp(w, "Description:") == 0) + skip2eol(cp->fin); + else if (strlen(w) >= len_prompt && strncmp(&w[strlen(w)-len_prompt], cp->prompt, len_prompt) == 0) + state = DONE; + else if (strcmp(w, "BW") == 0) + state = BW; + break; + + case BW: + sscanf(w, "%d", &bandwidth); + bandwidth *= 1000; /* Kbit -> bytes/sec */ + bandwidth /= 8; + state = IN_REPORT; + break; + + } + } + alarm(0); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Extracted bandwidth: %d bytes/sec\n", bandwidth); + } +#endif + return bandwidth; +} + +#define SHOW_INT 1 +#define SHOW_FRAME 2 + +int +grab_cisco(intf_t *ip) +{ + int style; + int next_state; + int state = NOISE; + int skip = 0; + int i; + int namelen; + char *pw_prompt = NULL; + char *w; + int fd; + int fd2; + int nval = 0; + cisco_t *cp = ip->cp; + intf_t tmp; + int len_prompt = strlen(cp->prompt); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "grab_cisco(%s:%s):\n", cp->host, ip->interface); + } +#endif + + tmp.bandwidth = tmp.rate_in = tmp.rate_out = -1; + tmp.bytes_in = tmp.bytes_out = tmp.bytes_out_bcast = -1; + + if (cp->fin == NULL) { + fd = conn_cisco(cp); + if (fd < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "grab_cisco(%s:%s): connect failed: %s\n", + cp->host, ip->interface, netstrerror()); +#endif + return -1; + } + else { + cp->fin = fdopen (fd, "r"); + if ((fd2 = dup(fd)) < 0) { + perror("dup"); + exit(1); + } + cp->fout = fdopen (fd2, "w"); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "grab_cisco(%s:%s): connected fin=%d fout=%d", + cp->host, ip->interface, fileno(cp->fin), fileno(cp->fout)); + if (cp->username != NULL) + fprintf(stderr, " username=%s", cp->username); + else + fprintf(stderr, " NO username"); + if (cp->passwd != NULL) + fprintf(stderr, " passwd=%s", cp->passwd); + else + fprintf(stderr, " NO passwd"); + fputc('\n', stderr); + } +#endif + + if (cp->username != NULL) { + /* + * Username stuff ... + */ + if (dousername(cp, &pw_prompt) == 0) { + fclose(cp->fin); + fclose(cp->fout); + cp->fin = cp->fout = NULL; + return -1; + } + } + if (cp->passwd != NULL) { + /* + * User-level password stuff ... + */ + if (dopasswd(cp, pw_prompt) == 0) { + fclose(cp->fin); + fclose(cp->fout); + cp->fin = cp->fout = NULL; + return -1; + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: \n"); + } +#endif + fprintf(cp->fout, "\n"); + fflush(cp->fout); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: terminal length 0\n"); + } +#endif + fprintf(cp->fout, "terminal length 0\n"); + fflush(cp->fout); + } + + timeout = 0; + signal(SIGALRM, onalarm); + /* + * Choice of timeout here is somewhat arbitrary ... for a long + * time this was 5 (seconds), but then testing with an entry + * level Model 800 ADSL router revealed that up to 20 seconds + * was required to generate the expected output. + */ + alarm(20); + + style = SHOW_INT; /* default Cisco command */ + if (ip->interface[0] == 's' && strchr(ip->interface, '.') != NULL) { + /* + * Frame-relay PVC on subinterface for s2/3.7 style interface name + */ + style = SHOW_FRAME; + if (ip->bandwidth == -2) { + /* + * one-trip initialzation ... need show int s2/3.7 to + * get bandwidth + */ + ip->bandwidth = get_fr_bw(cp, &ip->interface[1]); + } + tmp.bandwidth = ip->bandwidth; + if (tmp.bandwidth != -1) + nval++; + } + if (style == SHOW_FRAME) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: show frame pvc int s%s\n", &ip->interface[1]); + } +#endif + fprintf(cp->fout, "show frame pvc int s%s\n", &ip->interface[1]); + next_state = BYTES_IN; + } + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Send: show int %s\n", ip->interface); + } +#endif + fprintf(cp->fout, "show int %s\n", ip->interface); + } + fflush(cp->fout); + state = NOISE; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "Parse:"); + fflush(stderr); + } +#endif + while (state != DONE) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "[%s] ", statestr[state+1]); + fflush(stderr); + } +#endif + w = mygetwd(cp->fin, cp->prompt); + if (w == NULL || timeout) { + /* + * End of File (telenet timeout?) + * ... mark as closed, and try again at next request + */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "grab_cisco(%s:%s): forced disconnect fin=%d\n", + cp->host, ip->interface, fileno(cp->fin)); +#endif + fclose(cp->fin); + fclose(cp->fout); + cp->fin = cp->fout = NULL; + alarm(0); + return -1; + } + switch (state) { + + case NOISE: + for (i = 0; i < num_intf_tab; i++) { + namelen = strlen(intf_tab[i].name); + if (strncmp(w, intf_tab[i].name, namelen) == 0) { + state = IN_REPORT; + break; + } + } + break; + + case IN_REPORT: + if (strcmp(w, "Description:") == 0) + skip2eol(cp->fin); + if (strlen(w) >= len_prompt && strncmp(&w[strlen(w)-len_prompt], cp->prompt, len_prompt) == 0) + state = DONE; + else if (style == SHOW_INT) { + if (strcmp(w, "minute") == 0 || strcmp(w, "second") == 0) + state = RATE; + else if (strcmp(w, "input,") == 0) + state = BYTES_IN; + else if (strcmp(w, "output,") == 0) + state = BYTES_OUT; + else if (strcmp(w, "BW") == 0) + state = BW; + } + else if (style == SHOW_FRAME) { + if (strcmp(w, "bytes") == 0) { + if (next_state == BYTES_IN) { + state = BYTES_IN; + next_state = BYTES_OUT; + } + else if (next_state == BYTES_OUT) { + state = BYTES_OUT; + next_state = BYTES_OUT_BCAST; + } + else if (next_state == BYTES_OUT_BCAST) { + state = BYTES_OUT_BCAST; + next_state = IN_REPORT; + } + else + state = next_state; + } + } + break; + + case RATE: + if (strcmp(w, "input") == 0) { + skip = 1; + state = RATE_IN; + } + else if (strcmp(w, "output") == 0) { + skip = 1; + state = RATE_OUT; + } + break; + + case RATE_IN: + if (skip-- == 0) { + tmp.rate_in = atol(w) / 8; + nval++; + state = IN_REPORT; + } + break; + + case RATE_OUT: + if (skip-- == 0) { + tmp.rate_out = atol(w) / 8; + nval++; + state = IN_REPORT; + } + break; + + case BYTES_IN: + tmp.bytes_in = strtoull(w, NULL, 10); + nval++; + state = IN_REPORT; + break; + + case BYTES_OUT: + tmp.bytes_out = strtoull(w, NULL, 10); + nval++; + state = IN_REPORT; + break; + + case BYTES_OUT_BCAST: + tmp.bytes_out_bcast = strtoull(w, NULL, 10); + nval++; + state = IN_REPORT; + break; + + case BW: + sscanf(w, "%d", &tmp.bandwidth); + tmp.bandwidth *= 1000; /* Kbit -> bytes/sec */ + tmp.bandwidth /= 8; + nval++; + state = IN_REPORT; + break; + + } + } + alarm(0); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Extracted %d values ...\n", nval); + if (tmp.bandwidth != 0xffffffff) + fprintf(stderr, "bandwidth: %d bytes/sec\n", tmp.bandwidth); + else + fprintf(stderr, "bandwidth: ? bytes/sec\n"); + fprintf(stderr, "recent rate (bytes/sec):"); + if (tmp.rate_in != 0xffffffff) + fprintf(stderr, " %d in", tmp.rate_in); + else + fprintf(stderr, " ? in"); + if (tmp.rate_out != 0xffffffff) + fprintf(stderr, " %d out", tmp.rate_out); + else + fprintf(stderr, " ? out"); + fprintf(stderr, "\ntotal bytes:"); + if (tmp.bytes_in != 0xffffffffffffffffLL) + fprintf(stderr, " %llu in", (unsigned long long)tmp.bytes_in); + else + fprintf(stderr, " ? in"); + if (tmp.bytes_out != 0xffffffffffffffffLL) + fprintf(stderr, " %llu out", (unsigned long long)tmp.bytes_out); + else + fprintf(stderr, " ? out"); + if (tmp.bytes_out_bcast != 0xffffffffffffffffLL) + fprintf(stderr, " %llu out_bcast", (unsigned long long)tmp.bytes_out_bcast); + else + fprintf(stderr, " ? out_bcast"); + fprintf(stderr, "\n\n"); + } +#endif + + /* pretend this is atomic */ + ip->bandwidth = tmp.bandwidth; + ip->rate_in = tmp.rate_in; + ip->rate_out = tmp.rate_out; + ip->bytes_in = tmp.bytes_in; + ip->bytes_out = tmp.bytes_out; + ip->bytes_out_bcast = tmp.bytes_out_bcast; + + return nval; +} diff --git a/src/pmdas/darwin/GNUmakefile b/src/pmdas/darwin/GNUmakefile new file mode 100644 index 0000000..a2fd3a7 --- /dev/null +++ b/src/pmdas/darwin/GNUmakefile @@ -0,0 +1,69 @@ +# +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = darwin +DOMAIN = DARWIN +CMDTARGET = pmdadarwin +LIBTARGET = pmda_darwin.dylib +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "darwin 78 dso darwin_init $(PMDADIR)/$(LIBTARGET)" + +CFILES = pmda.c kernel.c disk.c network.c +HFILES = disk.h network.h + +LSRCFILES = help root pmns + +FRAMEWORKS = -framework CoreFoundation -framework IOKit +LLDLIBS = $(PCP_PMDALIB) $(FRAMEWORKS) + +LDIRT = *.log *.dir *.pag root_darwin domain.h + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "darwin" +build-me: root_darwin domain.h $(LIBTARGET) $(CMDTARGET) help.dir help.pag + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help.dir help.pag $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root_darwin $(PCP_VAR_DIR)/pmns/root_darwin +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +help.dir help.pag: help root_darwin + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_darwin -v 2 -o help < help + +root_darwin: ../../pmns/stdpmid root pmns + rm -f root_darwin + sed -e 's;;"../../pmns/stdpmid";' root_darwin + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/darwin/disk.c b/src/pmdas/darwin/disk.c new file mode 100644 index 0000000..5d175b3 --- /dev/null +++ b/src/pmdas/darwin/disk.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#define IOKIT 1 +#include +#include +#include +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "disk.h" + +/* + * Ensure we have space for the next device in our pre-allocated + * device pool. If not, make some or pass on the error. + */ +static int +check_stats_size(struct diskstats *stats, int count) +{ + if (count > stats->highwater) { + stats->highwater++; + stats->disks = realloc(stats->disks, + stats->highwater * sizeof(struct diskstat)); + if (!stats->disks) { + stats->highwater = 0; + return -ENOMEM; + } + } + return 0; +} + +/* + * Insert all disks into the global disk instance domain. + */ +static int +update_disk_indom(struct diskstats *all, int count, pmdaIndom *indom) +{ + int i; + + if (count > 0 && count != indom->it_numinst) { + i = sizeof(pmdaInstid) * count; + if ((indom->it_set = realloc(indom->it_set, i)) == NULL) { + indom->it_numinst = 0; + return -ENOMEM; + } + } + for (i = 0; i < count; i++) { + indom->it_set[i].i_name = all->disks[i].name; + indom->it_set[i].i_inst = i; + } + indom->it_numinst = count; + return 0; +} + +/* + * Update the global counters with values from one disk. + */ +static void +update_disk_totals(struct diskstats *all, struct diskstat *disk) +{ + all->read += disk->read; + all->write += disk->write; + all->read_bytes += disk->read_bytes; + all->write_bytes += disk->write_bytes; + all->blkread += disk->read_bytes / disk->blocksize; + all->blkwrite += disk->write_bytes / disk->blocksize; + all->read_time += disk->read_time; + all->write_time += disk->write_time; +} + +static void +clear_disk_totals(struct diskstats *all) +{ + all->read = 0; + all->write = 0; + all->read_bytes = 0; + all->write_bytes = 0; + all->blkread = 0; + all->blkwrite = 0; + all->read_time = 0; + all->write_time = 0; +} + +/* + * Update the counters associated with a single disk. + */ +static void +update_disk_stats(struct diskstat *disk, + CFDictionaryRef pproperties, CFDictionaryRef properties) +{ + CFDictionaryRef statistics; + CFStringRef name; + CFNumberRef number; + + memset(disk, 0, sizeof(struct diskstat)); + + /* Get name from the drive properties */ + name = (CFStringRef) CFDictionaryGetValue(pproperties, + CFSTR(kIOBSDNameKey)); + if(name == NULL) + return; /* Not much we can do with no name */ + + CFStringGetCString(name, disk->name, DEVNAMEMAX, + CFStringGetSystemEncoding()); + + /* Get the blocksize from the drive properties */ + number = (CFNumberRef) CFDictionaryGetValue(pproperties, + CFSTR(kIOMediaPreferredBlockSizeKey)); + if(number == NULL) + return; /* Not much we can do with no number */ + CFNumberGetValue(number, kCFNumberSInt64Type, &disk->blocksize); + + /* Get the statistics from the device properties. */ + statistics = (CFDictionaryRef) CFDictionaryGetValue(properties, + CFSTR(kIOBlockStorageDriverStatisticsKey)); + if (statistics) { + number = (CFNumberRef) CFDictionaryGetValue(statistics, + CFSTR(kIOBlockStorageDriverStatisticsReadsKey)); + if (number) + CFNumberGetValue(number, kCFNumberSInt64Type, + &disk->read); + number = (CFNumberRef) CFDictionaryGetValue(statistics, + CFSTR(kIOBlockStorageDriverStatisticsWritesKey)); + if (number) + CFNumberGetValue(number, kCFNumberSInt64Type, + &disk->write); + number = (CFNumberRef) CFDictionaryGetValue(statistics, + CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)); + if (number) + CFNumberGetValue(number, kCFNumberSInt64Type, + &disk->read_bytes); + number = (CFNumberRef) CFDictionaryGetValue(statistics, + CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)); + if (number) + CFNumberGetValue(number, kCFNumberSInt64Type, + &disk->write_bytes); + number = (CFNumberRef) CFDictionaryGetValue(statistics, + CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)); + if (number) + CFNumberGetValue(number, kCFNumberSInt64Type, + &disk->read_time); + number = (CFNumberRef) CFDictionaryGetValue(statistics, + CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)); + if (number) + CFNumberGetValue(number, kCFNumberSInt64Type, + &disk->write_time); + } +} + +static int +update_disk(diskstats_t *stats, io_registry_entry_t drive, int index) +{ + io_registry_entry_t device; + CFDictionaryRef pproperties, properties; + int status; + + /* Get the drives parent, from which we get statistics. */ + status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &device); + if (status != KERN_SUCCESS) + return -oserror(); + + if (!IOObjectConformsTo(device, "IOBlockStorageDriver")) { + IOObjectRelease(device); + return 0; + } + + /* Obtain the drive properties. */ + pproperties = 0; + status = IORegistryEntryCreateCFProperties(drive, + (CFMutableDictionaryRef *)&pproperties, + kCFAllocatorDefault, kNilOptions); + if (status != KERN_SUCCESS) { + IOObjectRelease(device); + return -oserror(); + } + + /* Obtain the device properties. */ + properties = 0; + status = IORegistryEntryCreateCFProperties(device, + (CFMutableDictionaryRef *)&properties, + kCFAllocatorDefault, kNilOptions); + if (status != KERN_SUCCESS) { + IOObjectRelease(device); + return -oserror(); + } + + /* Make space to store the actual values, then go get them. */ + status = check_stats_size(stats, index + 1); + if (status < 0) { + IOObjectRelease(device); + } else { + update_disk_stats(&stats->disks[index], pproperties, properties); + update_disk_totals(stats, &stats->disks[index]); + } + CFRelease(pproperties); + CFRelease(properties); + return status; +} + +int +refresh_disks(struct diskstats *stats, pmdaIndom *indom) +{ + io_registry_entry_t drive; + CFMutableDictionaryRef match; + int i, status; + static int inited = 0; + static mach_port_t mach_master_port; + static io_iterator_t mach_device_list; + + if (!inited) { + /* Get ports and services for device statistics. */ + if (IOMasterPort(bootstrap_port, &mach_master_port)) { + fprintf(stderr, "%s: IOMasterPort error\n", __FUNCTION__); + return -oserror(); + } + memset(stats, 0, sizeof(struct diskstats)); + inited = 1; + } + + /* Get an interator for IOMedia objects (drives). */ + match = IOServiceMatching("IOMedia"); + CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); + status = IOServiceGetMatchingServices(mach_master_port, + match, &mach_device_list); + if (status != KERN_SUCCESS) { + fprintf(stderr, "%s: IOServiceGetMatchingServices error\n", + __FUNCTION__); + return -oserror(); + } + + indom->it_numinst = 0; + clear_disk_totals(stats); + for (i = 0; (drive = IOIteratorNext(mach_device_list)) != 0; i++) { + status = update_disk(stats, drive, i); + if (status) + break; + IOObjectRelease(drive); + } + IOIteratorReset(mach_device_list); + + if (!status) + status = update_disk_indom(stats, i, indom); + return status; +} diff --git a/src/pmdas/darwin/disk.h b/src/pmdas/darwin/disk.h new file mode 100644 index 0000000..c3579ff --- /dev/null +++ b/src/pmdas/darwin/disk.h @@ -0,0 +1,55 @@ +/* + * Disk statistics types + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define DEVNAMEMAX 255 /* largest device name we allow */ + +/* + * Per-device statistics + */ +typedef struct diskstat { + __uint64_t read; + __uint64_t write; + __uint64_t read_bytes; + __uint64_t write_bytes; + __uint64_t read_time; + __uint64_t write_time; + __uint64_t blocksize; + char name[DEVNAMEMAX + 1]; +} diskstat_t; + +/* + * Global statistics. + * + * We avoid continually realloc'ing memory by keeping track + * of the maximum number of devices we've allocated space for + * so far, and only realloc new space if we go beyond that. + */ +typedef struct diskstats { + __uint64_t read; + __uint64_t write; + __uint64_t read_bytes; + __uint64_t write_bytes; + __uint64_t blkread; + __uint64_t blkwrite; + __uint64_t read_time; + __uint64_t write_time; + int highwater; /* largest number of devices seen so far */ + diskstat_t *disks; /* space for highwater number of devices */ +} diskstats_t; + diff --git a/src/pmdas/darwin/help b/src/pmdas/darwin/help new file mode 100644 index 0000000..ef8deee --- /dev/null +++ b/src/pmdas/darwin/help @@ -0,0 +1,199 @@ +# +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# MacOS X PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# +@ kernel.uname.release release level of the running kernel +@ kernel.uname.version version level (build number) and build date of the running kernel +@ kernel.uname.sysname name of the implementation of the operating system +@ kernel.uname.machine name of the hardware type the system is running on +@ kernel.uname.nodename host name of this node on the network + +@ kernel.all.cpu.user total user time for all processors +@ kernel.all.cpu.nice total nice time for all processors +@ kernel.all.cpu.sys total system time for all processors +@ kernel.all.cpu.idle total idle time for all processors +@ kernel.all.load 1, 5 and 15 minute load average +@ kernel.all.uptime time the current kernel has been running +@ kernel.all.hz value of HZ (jiffies/second) for the currently running kernel +@ hinv.ncpu number of processors +@ kernel.percpu.cpu.user percpu user processor time metric +@ kernel.percpu.cpu.nice percpu nice user processor time metric +@ kernel.percpu.cpu.sys percpu system processor time metric +@ kernel.percpu.cpu.idle percpu idle processor time metric + +@ hinv.physmem total system memory +@ hinv.pagesize system memory page size +@ mem.physmem total system memory metric +@ mem.freemem total pages free in the system +@ mem.active the total pages currently in use and pageable +@ mem.inactive the total pages on the inactive list +@ mem.pages.freemem total number of free pages in the system +@ mem.pages.active the number of pages currently in use and pageable +@ mem.pages.inactive the number of pages on the inactive list +@ mem.pages.reactivated the number of pages that have been moved from inactive to active list +@ mem.pages.wired the total number of pages wired down (cannot be paged out) +@ mem.pages.faults the number of times the "vm_fault" routine has been called +@ mem.pages.cow_faults the number of faults that caused a page to be copied +@ mem.pages.zero_filled the number of pages that have been zero-filled on demand +@ mem.pageins the number of requests for pages from a pager +@ mem.pageouts the number of pages that have been paged out +@ mem.cache_hits the number of object cache hits +@ mem.cache_lookups the number of object cache lookups +@ mem.util.wired wired memory +@ mem.util.active active memory +@ mem.util.inactive inactive memory +@ mem.util.free free memory + +@ hinv.nfilesys number of file systems currently mounted +@ filesys.capacity total capacity of mounted filesystem (Kbytes) +@ filesys.used total space used on mounted filesystem (Kbytes) +@ filesys.free total space free on mounted filesystem (Kbytes) +@ filesys.usedfiles number of inodes allocated on mounted filesystem +@ filesys.freefiles number of unallocated inodes on mounted filesystem +@ filesys.mountdir file system mount point +@ filesys.full percentage of filesystem in use +@ filesys.blocksize size of each block on mounted filesystem (Bytes) +@ filesys.avail total space free to non-superusers on mounted filesystem (Kbytes) +@ filesys.type filesystem type name for each mounted filesystem + +@ hinv.ndisk number of disks in the system +@ disk.dev.read per-disk read operations +Cumulative number of disk read operations since system boot time (subject +to counter wrap). + +@ disk.dev.write per-disk write operations +Cumulative number of disk write operations since system boot time (subject +to counter wrap). + +@ disk.dev.total per-disk total (read+write) operations +Cumulative number of disk read and write operations since system boot +time (subject to counter wrap). + +@ disk.dev.read_bytes per-disk count of bytes read +@ disk.dev.write_bytes per-disk count of bytes written +@ disk.dev.total_bytes per-disk count bytes read and written +@ disk.dev.blkread per-disk block read operations +Cumulative number of disk block read operations since system boot time +(subject to counter wrap). + +@ disk.dev.blkwrite per-disk block write operations +Cumulative number of disk block write operations since system boot time +(subject to counter wrap). + +@ disk.dev.blktotal per-disk total (read+write) block operations +Cumulative number of disk block read and write operations since system +boot time (subject to counter wrap). + +@ disk.dev.read_time i dunno either +@ disk.dev.write_time i dunno either +@ disk.dev.total_time i dunno either +@ disk.all.read_time i dunno either +@ disk.all.write_time i dunno either +@ disk.all.total_time i dunno either + +@ disk.all.read total read operations, summed for all disks +Cumulative number of disk read operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.write total write operations, summed for all disks +Cumulative number of disk read operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.total total read and write operations, summed for all disks +Cumulative number of disk read and write operations since system boot +time (subject to counter wrap), summed over all disk devices. + +@ disk.all.blkread block read operations, summed for all disks +Cumulative number of disk block read operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.blkwrite block write operations, summed for all disks +Cumulative number of disk block write operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.blktotal total (read+write) block operations, summed for all disks +Cumulative number of disk block read and write operations since system +boot time (subject to counter wrap), summed over all disk devices. + +@ disk.all.read_bytes count of bytes read for all disk devices +@ disk.all.write_bytes count of bytes written for all disk devices +@ disk.all.total_bytes count of bytes read and written for all disk devices + +@ network.interface.in.bytes network receive read bytes +@ network.interface.in.packets network receive read packets +@ network.interface.in.errors network receive read errors +@ network.interface.in.drops connections dropped on input +@ network.interface.in.mcasts network receive multicasts +@ network.interface.out.bytes network send write bytes +@ network.interface.out.packets network send write packets +@ network.interface.out.errors network send write errors +@ network.interface.out.mcasts network send multicasts +@ network.interface.collisions network send collisions for CDMA interfaces +@ network.interface.mtu maximum transmission size for network interfaces +@ network.interface.baudrate line speed for network interfaces +@ network.interface.total.bytes total network bytes received and sent +@ network.interface.total.packets total network packets received and sent +@ network.interface.total.errors total network errors on receive and send +@ network.interface.total.drops total network connections dropped +@ network.interface.total.mcasts total network multicasts + +@ nfs3.client.calls +@ nfs3.client.reqs +@ nfs3.server.calls +@ nfs3.server.reqs +@ rpc.client.rpccnt +@ rpc.client.rpcretrans +@ rpc.client.rpctimeouts +@ rpc.client.rpcinvalid +@ rpc.client.rpcunexpected +@ rpc.client.attrcache.hits +@ rpc.client.attrcache.misses +@ rpc.client.lookupcache.hits +@ rpc.client.lookupcache.misses +@ rpc.client.biocache.read.hits +@ rpc.client.biocache.read.misses +@ rpc.client.biocache.write.hits +@ rpc.client.biocache.write.misses +@ rpc.client.biocache.readlink.hits +@ rpc.client.biocache.readlink.misses +@ rpc.client.biocache.readdir.hits +@ rpc.client.biocache.readdir.misses +@ rpc.client.direofcache.hits +@ rpc.client.direofcache.misses +@ rpc.server.retfailed +@ rpc.server.faults +@ rpc.server.cache.inprog +@ rpc.server.cache.nonidem +@ rpc.server.cache.idem +@ rpc.server.cache.misses +@ rpc.server.vopwrites +@ rpc.server.pageins +@ rpc.server.pageouts diff --git a/src/pmdas/darwin/kernel.c b/src/pmdas/darwin/kernel.c new file mode 100644 index 0000000..57921c9 --- /dev/null +++ b/src/pmdas/darwin/kernel.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +extern mach_port_t mach_host; +extern int mach_hertz; + +int +refresh_vmstat(struct vm_statistics *vmstat) +{ + int error, info = HOST_VM_INFO; + natural_t count = HOST_VM_INFO_COUNT; + + error = host_statistics(mach_host, info, (host_info_t)vmstat, &count); + return (error != KERN_SUCCESS) ? -oserror() : 0; +} + +int +refresh_cpuload(struct host_cpu_load_info *cpuload) +{ + int error, info = HOST_CPU_LOAD_INFO; + natural_t count = HOST_CPU_LOAD_INFO_COUNT; + + error = host_statistics(mach_host, info, (host_info_t)cpuload, &count); + return (error != KERN_SUCCESS) ? -oserror() : 0; +} + +int +refresh_uname(struct utsname *utsname) +{ + return (uname(utsname) == -1) ? -oserror() : 0; +} + +int +refresh_hertz(unsigned int *hertz) +{ + int mib[2] = { CTL_KERN, KERN_CLOCKRATE }; + size_t size = sizeof(struct clockinfo); + struct clockinfo clockrate; + + if (sysctl(mib, 2, &clockrate, &size, NULL, 0) == -1) + return -oserror(); + *hertz = clockrate.hz; + return 0; +} + +int +refresh_loadavg(float *loadavg) +{ + int mib[2] = { CTL_VM, VM_LOADAVG }; + size_t size = sizeof(struct loadavg); + struct loadavg loadavgs; + + if (sysctl(mib, 2, &loadavgs, &size, NULL, 0) == -1) + return -oserror(); + loadavg[0] = (float)loadavgs.ldavg[0] / (float)loadavgs.fscale; + loadavg[1] = (float)loadavgs.ldavg[1] / (float)loadavgs.fscale; + loadavg[2] = (float)loadavgs.ldavg[2] / (float)loadavgs.fscale; + return 0; +} + +int +refresh_uptime(unsigned int *uptime) +{ + static struct timeval boottime; + struct timeval timediff; + + if (!boottime.tv_sec) { + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t size = sizeof(struct timeval); + + if (sysctl(mib, 2, &boottime, &size, NULL, 0) == -1) + return -oserror(); + } + + __pmtimevalNow(&timediff); + timediff.tv_usec -= boottime.tv_usec; + if (timediff.tv_usec < 0) { + timediff.tv_usec += 1000000; + timediff.tv_sec--; + } + timediff.tv_sec -= boottime.tv_sec; + + *uptime = timediff.tv_sec; + return 0; +} + +int +refresh_cpus(struct processor_cpu_load_info **cpuload, pmdaIndom *indom) +{ + natural_t ncpu, icount; + processor_info_array_t iarray; + struct processor_cpu_load_info *cpuinfo; + int error, i, info = PROCESSOR_CPU_LOAD_INFO; + + error = host_processor_info(mach_host, info, &ncpu, &iarray, &icount); + if (error != KERN_SUCCESS) + return -oserror(); + + cpuinfo = (struct processor_cpu_load_info *)iarray; + if (ncpu != indom->it_numinst) { + char name[16]; /* 8 is real max atm, but be conservative */ + + error = -ENOMEM; + i = sizeof(unsigned long) * CPU_STATE_MAX * ncpu; + if ((*cpuload = realloc(*cpuload, i)) == NULL) + goto vmdealloc; + + i = sizeof(pmdaInstid) * ncpu; + if ((indom->it_set = realloc(indom->it_set, i)) == NULL) { + free(*cpuload); + *cpuload = NULL; + indom->it_numinst = 0; + goto vmdealloc; + } + + for (i = 0; i < ncpu; i++) { + snprintf(name, sizeof(name), "cpu%d", i); + indom->it_set[i].i_name = strdup(name); + indom->it_set[i].i_inst = i; + } + indom->it_numinst = ncpu; + } + + error = 0; + for (i = 0; i < ncpu; i++) + memcpy(&(*cpuload)[i], &cpuinfo[i], + sizeof(unsigned long) * CPU_STATE_MAX); + +vmdealloc: + vm_deallocate(mach_host, (vm_address_t)iarray, icount); + return error; +} + +int +refresh_filesys(struct statfs **filesys, pmdaIndom *indom) +{ + int i, count = getmntinfo(filesys, MNT_NOWAIT); + + if (count < 0) { + indom->it_numinst = 0; + indom->it_set = NULL; + return -oserror(); + } + if (count > 0 && count != indom->it_numinst) { + i = sizeof(pmdaInstid) * count; + if ((indom->it_set = realloc(indom->it_set, i)) == NULL) { + indom->it_numinst = 0; + return -ENOMEM; + } + } + for (i = 0; i < count; i++) { + indom->it_set[i].i_name = (*filesys)[i].f_mntfromname; + indom->it_set[i].i_inst = i; + } + indom->it_numinst = count; + return 0; +} + +#if 0 +int +refresh_hinv() +{ +sysctl... +hw.machine = Power Macintosh +hw.model = PowerMac4,2 +hw.busfrequency = 99837332 +hw.cpufrequency = 700000000 +hw.cachelinesize = 32 +hw.l1icachesize = 32768 +hw.l1dcachesize = 32768 +hw.l2settings = 2147483648 +hw.l2cachesize = 262144 +} +#endif diff --git a/src/pmdas/darwin/network.c b/src/pmdas/darwin/network.c new file mode 100644 index 0000000..bc134c6 --- /dev/null +++ b/src/pmdas/darwin/network.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2004,2006 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "network.h" + +extern char *pmProgname; +extern mach_port_t mach_master_port; + +void +init_network(void) +{ + +} + +/* + * Ensure we have space for the next interface in our pre-allocated + * interface stats pool. If not, make some or pass on the error. + */ +static int +check_stats_size(struct netstats *stats, int count) +{ + if (count > stats->highwater) { + stats->highwater++; + stats->interfaces = realloc(stats->interfaces, + stats->highwater * sizeof(struct ifacestat)); + if (!stats->interfaces) { + stats->highwater = 0; + return -ENOMEM; + } + } + return 0; +} + +/* + * Insert all interfaces into the global network instance domain. + */ +static int +update_network_indom(struct netstats *all, int count, pmdaIndom *indom) +{ + int i; + + if (count > 0 && count != indom->it_numinst) { + i = sizeof(pmdaInstid) * count; + if ((indom->it_set = realloc(indom->it_set, i)) == NULL) { + indom->it_numinst = 0; + return -ENOMEM; + } + } + for (i = 0; i < count; i++) { + indom->it_set[i].i_name = all->interfaces[i].name; + indom->it_set[i].i_inst = i; + } + indom->it_numinst = count; + return 0; +} + +int +refresh_network(struct netstats *stats, pmdaIndom *indom) +{ + int i = 0, status = 0; + + size_t n; + char *new_buf, *next, *end; + struct sockaddr_dl *sdl; + + static char *buf=NULL; + static size_t buf_len=0; + +#ifdef RTM_IFINFO2 + struct if_msghdr2 *ifm; + int mib[6] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0}; +#else + struct if_msghdr *ifm; + int mib[6] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; +#endif + + if( sysctl( mib, 6, NULL, &n, NULL, 0 ) < 0 ) { + /* unable to query buffer size */ + fprintf( stderr, "%s: get net mib buf len failed\n", pmProgname ); + return -ENXIO; + } + if( n > buf_len ) { + if( (new_buf = malloc(n)) == NULL ) { + /* unable to malloc buf */ + fprintf( stderr, "%s: net mib buf malloc failed\n", pmProgname ); + return -ENXIO; + } else { + if( buf != NULL ) free( buf ); + buf = new_buf; + buf_len = n; + } + } + if( sysctl( mib, 6, buf, &n, NULL, 0 ) < 0 ) { + /* unable to copy-in buffer */ + fprintf( stderr, "%s: net mib buf read failed\n", pmProgname ); + return -ENXIO; + } + + for( next = buf, i=0, end = buf + n; next < end; ) { + +#ifdef RTM_IFINFO2 + ifm = (struct if_msghdr2 *)next; + next += ifm->ifm_msglen; + if( ifm->ifm_type == RTM_IFINFO2 ) { +#else + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + if( ifm->ifm_type == RTM_IFINFO ) { +#endif + + status = check_stats_size(stats, i + 1); + if (status < 0) break; + + sdl = (struct sockaddr_dl *)(ifm + 1); + n = sdl->sdl_nlen < IFNAMEMAX ? sdl->sdl_nlen : IFNAMEMAX; + strncpy( stats->interfaces[i].name, sdl->sdl_data, n ); + stats->interfaces[i].name[n] = 0; + + stats->interfaces[i].mtu = ifm->ifm_data.ifi_mtu; + stats->interfaces[i].baudrate = ifm->ifm_data.ifi_baudrate; + stats->interfaces[i].ipackets = ifm->ifm_data.ifi_ipackets; + stats->interfaces[i].ierrors = ifm->ifm_data.ifi_ierrors; + stats->interfaces[i].opackets = ifm->ifm_data.ifi_opackets; + stats->interfaces[i].oerrors = ifm->ifm_data.ifi_oerrors; + stats->interfaces[i].collisions = ifm->ifm_data.ifi_collisions; + stats->interfaces[i].ibytes = ifm->ifm_data.ifi_ibytes; + stats->interfaces[i].obytes = ifm->ifm_data.ifi_obytes; + stats->interfaces[i].imcasts = ifm->ifm_data.ifi_imcasts; + stats->interfaces[i].omcasts = ifm->ifm_data.ifi_omcasts; + stats->interfaces[i].iqdrops = ifm->ifm_data.ifi_iqdrops; + i++; + } + } + if (!status) update_network_indom(stats, i, indom); + return status; +} + +int +refresh_nfs(struct nfsstats *stats) +{ + int name[3]; + size_t length = sizeof(struct nfsstats); + static int nfstype = -1; + + if (nfstype == -1) { + struct vfsconf vfsconf; + + if (getvfsbyname("nfs", &vfsconf) == -1) + return -oserror(); + nfstype = vfsconf.vfc_typenum; + } + + name[0] = CTL_VFS; + name[1] = nfstype; + name[0] = NFS_NFSSTATS; + if (sysctl(name, 3, stats, &length, NULL, 0) == -1) + return -oserror(); + stats->biocache_reads -= stats->read_bios; + stats->biocache_writes -= stats->write_bios; + stats->biocache_readlinks -= stats->readlink_bios; + stats->biocache_readdirs -= stats->readdir_bios; + return 0; +} diff --git a/src/pmdas/darwin/network.h b/src/pmdas/darwin/network.h new file mode 100644 index 0000000..985ec9a --- /dev/null +++ b/src/pmdas/darwin/network.h @@ -0,0 +1,57 @@ +/* + * Network interface statistics types + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#define IFNAMEMAX 16 /* largest interface name we allow */ + +/* + * Per-interface statistics + */ +typedef struct ifacestat { + __uint64_t mtu; /* maximum transmission unit */ + __uint64_t baudrate; /* linespeed */ + __uint64_t ipackets; /* packets received on interface */ + __uint64_t ierrors; /* input errors on interface */ + __uint64_t opackets; /* packets sent on interface */ + __uint64_t oerrors; /* output errors on interface */ + __uint64_t collisions; /* collisions on csma interfaces */ + __uint64_t ibytes; /* total number of octets received */ + __uint64_t obytes; /* total number of octets sent */ + __uint64_t imcasts; /* packets received via multicast */ + __uint64_t omcasts; /* packets sent via multicast */ + __uint64_t iqdrops; /* dropped on input, this interface */ + char name[IFNAMEMAX + 1]; +} ifacestat_t; + +/* + * Global statistics. + * + * We avoid continually realloc'ing memory by keeping track + * of the maximum number of interfaces we've allocated space + * for so far, and only realloc new space if we go beyond that. + */ +typedef struct netstats { + int highwater; /* largest number of interfaces seen so far */ + ifacestat_t *interfaces; /* space for highwater number of interfaces */ +} netstats_t; + diff --git a/src/pmdas/darwin/pmda.c b/src/pmdas/darwin/pmda.c new file mode 100644 index 0000000..e000b11 --- /dev/null +++ b/src/pmdas/darwin/pmda.c @@ -0,0 +1,1268 @@ +/* + * MacOS X kernel PMDA + * "darwin" is easier to type than "macosx", especially for Aussies. ;-) + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" + +#include "disk.h" +#include "network.h" + + +#define page_count_to_kb(x) (((__uint64_t)(x) << mach_page_shift) >> 10) +#define page_count_to_mb(x) (((__uint64_t)(x) << mach_page_shift) >> 20) + +static pmdaInterface dispatch; +static int _isDSO = 1; /* =0 I am a daemon */ +static char *username; + +mach_port_t mach_host = 0; +vm_size_t mach_page_size = 0; +unsigned int mach_page_shift = 0; + +unsigned int mach_hertz = 0; +extern int refresh_hertz(unsigned int *); + +int mach_uname_error = 0; +struct utsname mach_uname = { { 0 } }; +extern int refresh_uname(struct utsname *); + +int mach_loadavg_error = 0; +float mach_loadavg[3] = { 0,0,0 }; +extern int refresh_loadavg(float *); + +int mach_cpuload_error = 0; +struct host_cpu_load_info mach_cpuload = { { 0 } }; +extern int refresh_cpuload(struct host_cpu_load_info *); + +int mach_vmstat_error = 0; +struct vm_statistics mach_vmstat = { 0 }; +extern int refresh_vmstat(struct vm_statistics *); + +int mach_fs_error = 0; +struct statfs *mach_fs = NULL; +extern int refresh_filesys(struct statfs **, pmdaIndom *); + +int mach_disk_error = 0; +struct diskstats mach_disk = { 0 }; +extern int refresh_disks(struct diskstats *, pmdaIndom *); + +int mach_cpu_error = 0; +struct processor_cpu_load_info *mach_cpu = NULL; +extern int refresh_cpus(struct processor_cpu_load_info **, pmdaIndom *); + +int mach_uptime_error = 0; +unsigned int mach_uptime = 0; +extern int refresh_uptime(unsigned int *); + +int mach_net_error = 0; +struct netstats mach_net = { 0 }; +extern int refresh_network(struct netstats *, pmdaIndom *); +extern void init_network(void); + +int mach_nfs_error = 0; +struct nfsstats mach_nfs = { 0 }; +extern int refresh_nfs(struct nfsstats *); + +/* + * Metric Instance Domains (statically initialized ones only) + */ +static pmdaInstid loadavg_indom_id[] = { + { 1, "1 minute" }, { 5, "5 minute" }, { 15, "15 minute" } +}; +#define LOADAVG_COUNT (sizeof(loadavg_indom_id)/sizeof(pmdaInstid)) + +static pmdaInstid nfs3_indom_id[] = { + { 0, "null" }, { 1, "getattr" }, { 2, "setattr" }, + { 3, "lookup" }, { 4, "access" }, { 5, "readlink" }, + { 6, "read" }, { 7, "write" }, { 8, "create" }, + { 9, "mkdir" }, { 10, "symlink" }, { 11, "mknod" }, + { 12, "remove" }, { 13, "rmdir" }, { 14, "rename" }, + { 15, "link" }, { 16, "readdir" }, { 17, "readdir+" }, + { 18, "statfs" }, { 19, "fsinfo" }, { 20, "pathconf" }, + { 21, "commit" }, { 22, "getlease" }, { 23, "vacate" }, + { 24, "evict" } +}; +#define NFS3_RPC_COUNT (sizeof(nfs3_indom_id)/sizeof(pmdaInstid)) + +/* + * Metric Instance Domain table + */ +enum { + LOADAVG_INDOM, /* 0 - 1, 5, 15 minute run queue averages */ + FILESYS_INDOM, /* 1 - set of all mounted filesystems */ + DISK_INDOM, /* 2 - set of all disk devices */ + CPU_INDOM, /* 3 - set of all processors */ + NETWORK_INDOM, /* 4 - set of all network interfaces */ + NFS3_INDOM, /* 6 - nfs v3 operations */ + NUM_INDOMS /* total number of instance domains */ +}; + +static pmdaIndom indomtab[] = { + { LOADAVG_INDOM, 3, loadavg_indom_id }, + { FILESYS_INDOM, 0, NULL }, + { DISK_INDOM, 0, NULL }, + { CPU_INDOM, 0, NULL }, + { NETWORK_INDOM, 0, NULL }, + { NFS3_INDOM, NFS3_RPC_COUNT, nfs3_indom_id }, +}; + +/* + * Fetch clusters and metric table + */ +enum { + CLUSTER_INIT = 0, /* 0 = values we know at startup */ + CLUSTER_VMSTAT, /* 1 = mach memory statistics */ + CLUSTER_KERNEL_UNAME, /* 2 = utsname information */ + CLUSTER_LOADAVG, /* 3 = run queue averages */ + CLUSTER_HINV, /* 4 = hardware inventory */ + CLUSTER_FILESYS, /* 5 = mounted filesystems */ + CLUSTER_CPULOAD, /* 6 = number of ticks in state */ + CLUSTER_DISK, /* 7 = disk device statistics */ + CLUSTER_CPU, /* 8 = per-cpu statistics */ + CLUSTER_UPTIME, /* 9 = system uptime in seconds */ + CLUSTER_NETWORK, /* 10 = networking statistics */ + CLUSTER_NFS, /* 11 = nfs filesystem statistics */ + NUM_CLUSTERS /* total number of clusters */ +}; + +static pmdaMetric metrictab[] = { + +/* hinv.pagesize */ + { &mach_page_size, + { PMDA_PMID(CLUSTER_INIT,0), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* kernel.all.hz */ + { &mach_hertz, + { PMDA_PMID(CLUSTER_INIT,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE) }, }, + +/* hinv.physmem */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(1,0,0,PM_SPACE_MBYTE,0,0) }, }, +/* mem.physmem */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,3), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.freemem */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,4), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.active */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,5), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.inactive */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,6), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.pages.free */ + { &mach_vmstat.free_count, + { PMDA_PMID(CLUSTER_VMSTAT,7), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.active */ + { &mach_vmstat.active_count, + { PMDA_PMID(CLUSTER_VMSTAT,8), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.inactive */ + { &mach_vmstat.inactive_count, + { PMDA_PMID(CLUSTER_VMSTAT,9), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.reactivated */ + { &mach_vmstat.reactivations, + { PMDA_PMID(CLUSTER_VMSTAT,10), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.wired */ + { &mach_vmstat.wire_count, + { PMDA_PMID(CLUSTER_VMSTAT,11), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.faults */ + { &mach_vmstat.faults, + { PMDA_PMID(CLUSTER_VMSTAT,12), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.cow_faults */ + { &mach_vmstat.cow_faults, + { PMDA_PMID(CLUSTER_VMSTAT,13), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pages.zero_filled */ + { &mach_vmstat.zero_fill_count, + { PMDA_PMID(CLUSTER_VMSTAT,14), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pageins */ + { &mach_vmstat.pageins, + { PMDA_PMID(CLUSTER_VMSTAT,15), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.pageouts */ + { &mach_vmstat.pageouts, + { PMDA_PMID(CLUSTER_VMSTAT,16), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.cache_hits */ + { &mach_vmstat.hits, + { PMDA_PMID(CLUSTER_VMSTAT,17), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.cache_lookups */ + { &mach_vmstat.lookups, + { PMDA_PMID(CLUSTER_VMSTAT,18), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* mem.util.wired */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,19), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.util.active */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,20), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.util.inactive */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,21), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* mem.util.free */ + { NULL, + { PMDA_PMID(CLUSTER_VMSTAT,22), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* kernel.uname.release */ + { mach_uname.release, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 23), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* kernel.uname.version */ + { mach_uname.version, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 24), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* kernel.uname.sysname */ + { mach_uname.sysname, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 25), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* kernel.uname.machine */ + { mach_uname.machine, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 26), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* kernel.uname.nodename */ + { mach_uname.nodename, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 27), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* pmda.uname */ + { NULL, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 28), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* pmda.version */ + { NULL, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 29), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* kernel.all.load */ + { NULL, + { PMDA_PMID(CLUSTER_LOADAVG,30), PM_TYPE_FLOAT, LOADAVG_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* hinv.nfilesys */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,31), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* filesys.capacity */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,32), PM_TYPE_U64, FILESYS_INDOM, + PM_SEM_DISCRETE, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* filesys.used */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,33), PM_TYPE_U64, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* filesys.free */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,34), PM_TYPE_U64, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* filesys.usedfiles */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,35), PM_TYPE_U32, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* filesys.freefiles */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,36), PM_TYPE_U32, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* filesys.mountdir */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,37), PM_TYPE_STRING, FILESYS_INDOM, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* filesys.full */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,38), PM_TYPE_DOUBLE, FILESYS_INDOM, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* filesys.blocksize */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,39), PM_TYPE_U32, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* filesys.avail */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,40), PM_TYPE_U64, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* filesys.type */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,41), PM_TYPE_STRING, FILESYS_INDOM, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* kernel.all.cpu.user */ + { NULL, + { PMDA_PMID(CLUSTER_CPULOAD,42), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* kernel.all.cpu.nice */ + { NULL, + { PMDA_PMID(CLUSTER_CPULOAD,43), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* kernel.all.cpu.sys */ + { NULL, + { PMDA_PMID(CLUSTER_CPULOAD,44), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* kernel.all.cpu.idle */ + { NULL, + { PMDA_PMID(CLUSTER_CPULOAD,45), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* hinv.ndisk */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,46), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* disk.dev.read */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,47), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.dev.write */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,48), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.dev.total */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,49), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.dev.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,50), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* disk.dev.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,51), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* disk.dev.total_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,52), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* disk.dev.blkread */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,53), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.dev.blkwrite */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,54), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.dev.blktotal */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,55), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.dev.read_time */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,56), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, +/* disk.dev.write_time */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,57), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, +/* disk.dev.total_time */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,58), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, +/* disk.all.read */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.all.write */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.all.total */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,61), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.all.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,62), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* disk.all.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,63), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* disk.all.total_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,64), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* disk.all.blkread */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,65), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.all.blkwrite */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,66), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.all.blktotal */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,67), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* disk.all.read_time */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,68), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, +/* disk.all.write_time */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,69), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, +/* disk.all.total_time */ + { NULL, + { PMDA_PMID(CLUSTER_DISK,70), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + +/* hinv.ncpu */ + { NULL, + { PMDA_PMID(CLUSTER_CPU,71), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* kernel.percpu.cpu.user */ + { NULL, + { PMDA_PMID(CLUSTER_CPU,72), PM_TYPE_U64, CPU_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* kernel.percpu.cpu.nice */ + { NULL, + { PMDA_PMID(CLUSTER_CPU,73), PM_TYPE_U64, CPU_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* kernel.percpu.cpu.sys */ + { NULL, + { PMDA_PMID(CLUSTER_CPU,74), PM_TYPE_U64, CPU_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* kernel.percpu.cpu.idle */ + { NULL, + { PMDA_PMID(CLUSTER_CPU,75), PM_TYPE_U64, CPU_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.uptime */ + { &mach_uptime, + { PMDA_PMID(CLUSTER_UPTIME,76), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,1,0,PM_TIME_SEC,PM_COUNT_ONE) }, }, + +/* network.interface.in.bytes */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,77), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* network.interface.in.packets */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,78), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.in.errors */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,79), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.in.drops */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,80), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.in.mcasts */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,81), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.out.bytes */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,82), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,0,PM_SPACE_BYTE,0) }, }, +/* network.interface.out.packets */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,83), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.out.errors */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,84), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.out.mcasts */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,85), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.collisions */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,86), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.mtu */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,87), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,0,PM_SPACE_BYTE,0) }, }, +/* network.interface.baudrate */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,88), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,0,PM_SPACE_BYTE,0) }, }, +/* network.interface.total.bytes */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,89), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,0,PM_SPACE_BYTE,0) }, }, +/* network.interface.total.packets */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,90), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.total.errors */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,91), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.total.drops */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,92), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* network.interface.total.mcasts */ + { NULL, + { PMDA_PMID(CLUSTER_NETWORK,93), PM_TYPE_U64, NETWORK_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* nfs3.client.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,94), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* nfs3.client.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,95), PM_TYPE_32, NFS3_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* nfs3.server.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,96), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* nfs3.server.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,97), PM_TYPE_32, NFS3_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.rpccnt */ + { &mach_nfs.rpcrequests, + { PMDA_PMID(CLUSTER_NFS,98), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.rpcretrans */ + { &mach_nfs.rpcretries, + { PMDA_PMID(CLUSTER_NFS,99), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.rpctimeouts */ + { &mach_nfs.rpctimeouts, + { PMDA_PMID(CLUSTER_NFS,100), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.rpcinvalid */ + { &mach_nfs.rpcinvalid, + { PMDA_PMID(CLUSTER_NFS,101), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.rpcunexpected */ + { &mach_nfs.rpcunexpected, + { PMDA_PMID(CLUSTER_NFS,102), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.attrcache.hits */ + { &mach_nfs.attrcache_hits, + { PMDA_PMID(CLUSTER_NFS,103), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.attrcache.misses */ + { &mach_nfs.attrcache_misses, + { PMDA_PMID(CLUSTER_NFS,104), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.lookupcache.hits */ + { &mach_nfs.lookupcache_hits, + { PMDA_PMID(CLUSTER_NFS,105), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.lookupcache.misses */ + { &mach_nfs.lookupcache_misses, + { PMDA_PMID(CLUSTER_NFS,106), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.read.hits */ + { &mach_nfs.read_bios, + { PMDA_PMID(CLUSTER_NFS,107), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.read.misses */ + { &mach_nfs.biocache_reads, + { PMDA_PMID(CLUSTER_NFS,108), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.write.hits */ + { &mach_nfs.write_bios, + { PMDA_PMID(CLUSTER_NFS,109), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.write.misses */ + { &mach_nfs.biocache_writes, + { PMDA_PMID(CLUSTER_NFS,110), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.readlink.hits */ + { &mach_nfs.readlink_bios, + { PMDA_PMID(CLUSTER_NFS,111), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.readlink.misses */ + { &mach_nfs.biocache_readlinks, + { PMDA_PMID(CLUSTER_NFS,112), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.readdir.hits */ + { &mach_nfs.readdir_bios, + { PMDA_PMID(CLUSTER_NFS,113), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.biocache.readdir.misses */ + { &mach_nfs.biocache_readdirs, + { PMDA_PMID(CLUSTER_NFS,114), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.direofcache.hits */ + { &mach_nfs.direofcache_hits, + { PMDA_PMID(CLUSTER_NFS,115), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.client.direofcache.misses */ + { &mach_nfs.direofcache_misses, + { PMDA_PMID(CLUSTER_NFS,116), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.retfailed */ + { &mach_nfs.srvrpc_errs, + { PMDA_PMID(CLUSTER_NFS,117), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.faults */ + { &mach_nfs.srvrpc_errs, + { PMDA_PMID(CLUSTER_NFS,118), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.cache.inprog */ + { &mach_nfs.srvcache_inproghits, + { PMDA_PMID(CLUSTER_NFS,119), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.cache.nonidem */ + { &mach_nfs.srvcache_nonidemdonehits, + { PMDA_PMID(CLUSTER_NFS,120), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.cache.idem */ + { &mach_nfs.srvcache_idemdonehits, + { PMDA_PMID(CLUSTER_NFS,121), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.cache.misses */ + { &mach_nfs.srvcache_misses, + { PMDA_PMID(CLUSTER_NFS,122), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.nqnfs.leases -- deprecated */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,123), PM_TYPE_NOSUPPORT, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.nqnfs.maxleases -- deprecated */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,124), PM_TYPE_NOSUPPORT, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.nqnfs.getleases -- deprecated */ + { NULL, + { PMDA_PMID(CLUSTER_NFS,125), PM_TYPE_NOSUPPORT, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.vopwrites */ + { &mach_nfs.srvvop_writes, + { PMDA_PMID(CLUSTER_NFS,126), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.pageins */ + { &mach_nfs.pageins, + { PMDA_PMID(CLUSTER_NFS,127), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* rpc.server.pageouts */ + { &mach_nfs.pageouts, + { PMDA_PMID(CLUSTER_NFS,128), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* filesys.maxfiles */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,129), PM_TYPE_U32, FILESYS_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +}; + +static void +darwin_refresh(int *need_refresh) +{ + if (need_refresh[CLUSTER_LOADAVG]) + mach_loadavg_error = refresh_loadavg(mach_loadavg); + if (need_refresh[CLUSTER_CPULOAD]) + mach_cpuload_error = refresh_cpuload(&mach_cpuload); + if (need_refresh[CLUSTER_VMSTAT]) + mach_vmstat_error = refresh_vmstat(&mach_vmstat); + if (need_refresh[CLUSTER_KERNEL_UNAME]) + mach_uname_error = refresh_uname(&mach_uname); + if (need_refresh[CLUSTER_FILESYS]) + mach_fs_error = refresh_filesys(&mach_fs, &indomtab[FILESYS_INDOM]); + if (need_refresh[CLUSTER_DISK]) + mach_disk_error = refresh_disks(&mach_disk, &indomtab[DISK_INDOM]); + if (need_refresh[CLUSTER_CPU]) + mach_cpu_error = refresh_cpus(&mach_cpu, &indomtab[CPU_INDOM]); + if (need_refresh[CLUSTER_UPTIME]) + mach_uptime_error = refresh_uptime(&mach_uptime); + if (need_refresh[CLUSTER_NETWORK]) + mach_net_error = refresh_network(&mach_net, &indomtab[NETWORK_INDOM]); + if (need_refresh[CLUSTER_NFS]) + mach_nfs_error = refresh_nfs(&mach_nfs); +} + +static inline int +fetch_loadavg(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (mach_loadavg_error) + return mach_loadavg_error; + switch (item) { + case 30: /* kernel.all.load */ + if (inst == 1) + atom->f = mach_loadavg[0]; + else if (inst == 5) + atom->f = mach_loadavg[1]; + else if (inst == 15) + atom->f = mach_loadavg[2]; + else + return PM_ERR_INST; + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_cpuload(unsigned int item, pmAtomValue *atom) +{ + if (mach_cpuload_error) + return mach_cpuload_error; + switch (item) { + case 42: /* kernel.all.cpu.user */ + atom->ull = LOAD_SCALE * (double) + mach_cpuload.cpu_ticks[CPU_STATE_USER] / mach_hertz; + return 1; + case 43: /* kernel.all.cpu.nice */ + atom->ull = LOAD_SCALE * (double) + mach_cpuload.cpu_ticks[CPU_STATE_NICE] / mach_hertz; + return 1; + case 44: /* kernel.all.cpu.sys */ + atom->ull = LOAD_SCALE * (double) + mach_cpuload.cpu_ticks[CPU_STATE_SYSTEM] / mach_hertz; + return 1; + case 45: /* kernel.all.cpu.idle */ + atom->ull = LOAD_SCALE * (double) + mach_cpuload.cpu_ticks[CPU_STATE_IDLE] / mach_hertz; + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_vmstat(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (mach_vmstat_error) + return mach_vmstat_error; + switch (item) { + case 2: /* hinv.physmem */ + atom->ul = (__uint32_t)page_count_to_mb( + mach_vmstat.free_count + mach_vmstat.wire_count + + mach_vmstat.active_count + mach_vmstat.inactive_count); + return 1; + case 3: /* mem.physmem */ + atom->ull = page_count_to_kb( + mach_vmstat.free_count + mach_vmstat.wire_count + + mach_vmstat.active_count + mach_vmstat.inactive_count); + return 1; + case 4: /* mem.freemem */ + atom->ull = page_count_to_kb(mach_vmstat.free_count); + return 1; + case 5: /* mem.active */ + atom->ull = page_count_to_kb(mach_vmstat.active_count); + return 1; + case 6: /* mem.inactive */ + atom->ull = page_count_to_kb(mach_vmstat.inactive_count); + return 1; + case 19: /* mem.util.wired */ + atom->ull = page_count_to_kb(mach_vmstat.wire_count); + return 1; + case 20: /* mem.util.active */ + atom->ull = page_count_to_kb(mach_vmstat.active_count); + return 1; + case 21: /* mem.util.inactive */ + atom->ull = page_count_to_kb(mach_vmstat.inactive_count); + return 1; + case 22: /* mem.util.free */ + atom->ull = page_count_to_kb(mach_vmstat.free_count); + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_uname(unsigned int item, pmAtomValue *atom) +{ + static char mach_uname_all[(_SYS_NAMELEN*5)+8]; + + if (mach_uname_error) + return mach_uname_error; + switch (item) { + case 28: /* pmda.uname */ + snprintf(mach_uname_all, sizeof(mach_uname_all), "%s %s %s %s %s", + mach_uname.sysname, mach_uname.nodename, + mach_uname.release, mach_uname.version, + mach_uname.machine); + atom->cp = mach_uname_all; + return 1; + case 29: /* pmda.version */ + atom->cp = pmGetConfig("PCP_VERSION"); + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_filesys(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + __uint64_t ull, used; + + if (mach_fs_error) + return mach_fs_error; + if (item == 31) { /* hinv.nfilesys */ + atom->ul = indomtab[FILESYS_INDOM].it_numinst; + return 1; + } + if (indomtab[FILESYS_INDOM].it_numinst == 0) + return 0; /* no values available */ + if (inst < 0 || inst >= indomtab[FILESYS_INDOM].it_numinst) + return PM_ERR_INST; + switch (item) { + case 32: /* filesys.capacity */ + ull = (__uint64_t)mach_fs[inst].f_blocks; + atom->ull = ull * mach_fs[inst].f_bsize >> 10; + return 1; + case 33: /* filesys.used */ + used = (__uint64_t)(mach_fs[inst].f_blocks - mach_fs[inst].f_bfree); + atom->ull = used * mach_fs[inst].f_bsize >> 10; + return 1; + case 34: /* filesys.free */ + ull = (__uint64_t)mach_fs[inst].f_bfree; + atom->ull = ull * mach_fs[inst].f_bsize >> 10; + return 1; + case 129: /* filesys.maxfiles */ + atom->ul = mach_fs[inst].f_files; + return 1; + case 35: /* filesys.usedfiles */ + atom->ul = mach_fs[inst].f_files - mach_fs[inst].f_ffree; + return 1; + case 36: /* filesys.freefiles */ + atom->ul = mach_fs[inst].f_ffree; + return 1; + case 37: /* filesys.mountdir */ + atom->cp = mach_fs[inst].f_mntonname; + return 1; + case 38: /* filesys.full */ + used = (__uint64_t)(mach_fs[inst].f_blocks - mach_fs[inst].f_bfree); + ull = used + (__uint64_t)mach_fs[inst].f_bavail; + atom->d = (100.0 * (double)used) / (double)ull; + return 1; + case 39: /* filesys.blocksize */ + atom->ul = mach_fs[inst].f_bsize; + return 1; + case 40: /* filesys.avail */ + ull = (__uint64_t)mach_fs[inst].f_bavail; + atom->ull = ull * mach_fs[inst].f_bsize >> 10; + return 1; + case 41: /* filesys.type */ + atom->cp = mach_fs[inst].f_fstypename; + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_disk(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (mach_disk_error) + return mach_disk_error; + if (item == 46) { /* hinv.ndisk */ + atom->ul = indomtab[DISK_INDOM].it_numinst; + return 1; + } + if (indomtab[DISK_INDOM].it_numinst == 0) + return 0; /* no values available */ + if (item < 59 && (inst < 0 || inst >= indomtab[DISK_INDOM].it_numinst)) + return PM_ERR_INST; + switch (item) { + case 47: /* disk.dev.read */ + atom->ull = mach_disk.disks[inst].read; + return 1; + case 48: /* disk.dev.write */ + atom->ull = mach_disk.disks[inst].write; + return 1; + case 49: /* disk.dev.total */ + atom->ull = mach_disk.disks[inst].read + mach_disk.disks[inst].write; + return 1; + case 50: /* disk.dev.read_bytes */ + atom->ull = mach_disk.disks[inst].read_bytes >> 10; + return 1; + case 51: /* disk.dev.write_bytes */ + atom->ull = mach_disk.disks[inst].write_bytes >> 10; + return 1; + case 52: /* disk.dev.total_bytes */ + atom->ull = (mach_disk.disks[inst].read_bytes + + mach_disk.disks[inst].write_bytes) >> 10; + return 1; + case 53: /* disk.dev.blkread */ + atom->ull = mach_disk.disks[inst].read_bytes / + mach_disk.disks[inst].blocksize; + return 1; + case 54: /* disk.dev.blkwrite */ + atom->ull = mach_disk.disks[inst].write_bytes / + mach_disk.disks[inst].blocksize; + return 1; + case 55: /* disk.dev.blktotal */ + atom->ull = (mach_disk.disks[inst].read_bytes + + mach_disk.disks[inst].write_bytes) / + mach_disk.disks[inst].blocksize; + return 1; + case 56: /* disk.dev.read_time */ + atom->ull = mach_disk.disks[inst].read_time; + return 1; + case 57: /* disk.dev.write_time */ + atom->ull = mach_disk.disks[inst].write_time; + return 1; + case 58: /* disk.dev.total_time */ + atom->ull = mach_disk.disks[inst].read_time + + mach_disk.disks[inst].write_time; + return 1; + case 59: /* disk.all.read */ + atom->ull = mach_disk.read; + return 1; + case 60: /* disk.all.write */ + atom->ull = mach_disk.write; + return 1; + case 61: /* disk.all.total */ + atom->ull = mach_disk.read + mach_disk.write; + return 1; + case 62: /* disk.all.read_bytes */ + atom->ull = mach_disk.read_bytes >> 10; + return 1; + case 63: /* disk.all.write_bytes */ + atom->ull = mach_disk.write_bytes >> 10; + return 1; + case 64: /* disk.all.total_bytes */ + atom->ull = (mach_disk.read_bytes + mach_disk.write_bytes) >> 10; + return 1; + case 65: /* disk.all.blkread */ + atom->ull = mach_disk.blkread; + return 1; + case 66: /* disk.all.blkwrite */ + atom->ull = mach_disk.blkwrite; + return 1; + case 67: /* disk.all.blktotal */ + atom->ull = mach_disk.blkread + mach_disk.blkwrite; + return 1; + case 68: /* disk.all.read_time */ + atom->ull = mach_disk.read_time; + return 1; + case 69: /* disk.all.write_time */ + atom->ull = mach_disk.write_time; + return 1; + case 70: /* disk.all.total_time */ + atom->ull = mach_disk.read_time + mach_disk.write_time; + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_cpu(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (mach_cpu_error) + return mach_cpu_error; + if (item == 71) { /* hinv.ncpu */ + atom->ul = indomtab[CPU_INDOM].it_numinst; + return 1; + } + if (indomtab[CPU_INDOM].it_numinst == 0) /* uh-huh. */ + return 0; /* no values available */ + if (inst < 0 || inst >= indomtab[CPU_INDOM].it_numinst) + return PM_ERR_INST; + switch (item) { + case 72: /* kernel.percpu.cpu.user */ + atom->ull = LOAD_SCALE * (double) + mach_cpu[inst].cpu_ticks[CPU_STATE_USER] / mach_hertz; + return 1; + case 73: /* kernel.percpu.cpu.nice */ + atom->ull = LOAD_SCALE * (double) + mach_cpu[inst].cpu_ticks[CPU_STATE_NICE] / mach_hertz; + return 1; + case 74: /* kernel.percpu.cpu.sys */ + atom->ull = LOAD_SCALE * (double) + mach_cpu[inst].cpu_ticks[CPU_STATE_SYSTEM] / mach_hertz; + return 1; + case 75: /* kernel.percpu.cpu.idle */ + atom->ull = LOAD_SCALE * (double) + mach_cpu[inst].cpu_ticks[CPU_STATE_IDLE] / mach_hertz; + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_network(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (mach_net_error) + return mach_net_error; + if (indomtab[NETWORK_INDOM].it_numinst == 0) + return 0; /* no values available */ + if (inst < 0 || inst >= indomtab[NETWORK_INDOM].it_numinst) + return PM_ERR_INST; + switch (item) { + case 77: /* network.interface.in.bytes */ + atom->ull = mach_net.interfaces[inst].ibytes; + return 1; + case 78: /* network.interface.in.packets */ + atom->ull = mach_net.interfaces[inst].ipackets; + return 1; + case 79: /* network.interface.in.errors */ + atom->ull = mach_net.interfaces[inst].ierrors; + return 1; + case 80: /* network.interface.in.drops */ + atom->ull = mach_net.interfaces[inst].iqdrops; + return 1; + case 81: /* network.interface.in.mcasts */ + atom->ull = mach_net.interfaces[inst].imcasts; + return 1; + case 82: /* network.interface.out.bytes */ + atom->ull = mach_net.interfaces[inst].obytes; + return 1; + case 83: /* network.interface.out.packets */ + atom->ull = mach_net.interfaces[inst].opackets; + return 1; + case 84: /* network.interface.out.errors */ + atom->ull = mach_net.interfaces[inst].oerrors; + return 1; + case 85: /* network.interface.out.mcasts */ + atom->ull = mach_net.interfaces[inst].omcasts; + return 1; + case 86: /* network.interface.collisions */ + atom->ull = mach_net.interfaces[inst].collisions; + return 1; + case 87: /* network.interface.mtu */ + atom->ull = mach_net.interfaces[inst].mtu; + return 1; + case 88: /* network.interface.baudrate */ + atom->ull = mach_net.interfaces[inst].baudrate; + return 1; + case 89: /* network.interface.total.bytes */ + atom->ull = mach_net.interfaces[inst].ibytes + + mach_net.interfaces[inst].obytes; + return 1; + case 90: /* network.interface.total.packets */ + atom->ull = mach_net.interfaces[inst].ipackets + + mach_net.interfaces[inst].opackets; + return 1; + case 91: /* network.interface.total.errors */ + atom->ull = mach_net.interfaces[inst].ierrors + + mach_net.interfaces[inst].oerrors; + return 1; + case 92: /* network.interface.total.drops */ + atom->ull = mach_net.interfaces[inst].iqdrops; + return 1; + case 93: /* network.interface.total.mcasts */ + atom->ull = mach_net.interfaces[inst].imcasts + + mach_net.interfaces[inst].omcasts; + return 1; + } + return PM_ERR_PMID; +} + +static inline int +fetch_nfs(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (mach_net_error) + return mach_net_error; + switch (item) { + case 94: /* nfs3.client.calls */ + for (atom->l = 0, inst = 0; inst < NFS3_RPC_COUNT; inst++) + atom->l += mach_nfs.rpccnt[inst]; + return 1; + case 95: /* nfs3.client.reqs */ + if (inst < 0 || inst >= NFS3_RPC_COUNT) + return PM_ERR_INST; + atom->l = mach_nfs.rpccnt[inst]; + return 1; + case 96: /* nfs3.server.calls */ + for (atom->l = 0, inst = 0; inst < NFS3_RPC_COUNT; inst++) + atom->l += mach_nfs.srvrpccnt[inst]; + return 1; + case 97: /* nfs3.server.reqs */ + if (inst < 0 || inst >= NFS3_RPC_COUNT) + return PM_ERR_INST; + atom->l = mach_nfs.srvrpccnt[inst]; + return 1; + case 123: /* rpc.server.nqnfs.leases -- deprecated */ + case 124: /* rpc.server.nqnfs.maxleases -- deprecated */ + case 125: /* rpc.server.nqnfs.getleases -- deprecated */ + return PM_ERR_APPVERSION; + } + return PM_ERR_PMID; +} + + +static int +darwin_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (mdesc->m_user) { + /* + * The metric value is extracted directly via the address specified + * in metrictab. Note: not all metrics support this - those that + * don't have NULL for the m_user field in their respective + * metrictab slot. + */ + switch (mdesc->m_desc.type) { + case PM_TYPE_32: atom->l = *(__int32_t *)mdesc->m_user; break; + case PM_TYPE_U32: atom->ul = *(__uint32_t *)mdesc->m_user; break; + case PM_TYPE_64: atom->ll = *(__int64_t *)mdesc->m_user; break; + case PM_TYPE_U64: atom->ull = *(__uint64_t *)mdesc->m_user; break; + case PM_TYPE_FLOAT: atom->f = *(float *)mdesc->m_user; break; + case PM_TYPE_DOUBLE: atom->d = *(double *)mdesc->m_user; break; + case PM_TYPE_STRING: atom->cp = (char *)mdesc->m_user; break; + case PM_TYPE_NOSUPPORT: return 0; + default: fprintf(stderr, + "Error in fetchCallBack: unsupported metric type %s\n", + pmTypeStr(mdesc->m_desc.type)); + return 0; + } + return 1; + } + + switch (idp->cluster) { + case CLUSTER_LOADAVG: return fetch_loadavg(idp->item, inst, atom); + case CLUSTER_CPULOAD: return fetch_cpuload(idp->item, atom); + case CLUSTER_VMSTAT: return fetch_vmstat(idp->item, inst, atom); + case CLUSTER_KERNEL_UNAME: return fetch_uname(idp->item, atom); + case CLUSTER_FILESYS: return fetch_filesys(idp->item, inst, atom); + case CLUSTER_DISK: return fetch_disk(idp->item, inst, atom); + case CLUSTER_CPU: return fetch_cpu(idp->item, inst, atom); + case CLUSTER_NETWORK: return fetch_network(idp->item, inst, atom); + case CLUSTER_NFS: return fetch_nfs(idp->item, inst, atom); + } + return 0; +} + +static int +darwin_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + __pmInDom_int *indomp = (__pmInDom_int *)&indom; + int need_refresh[NUM_CLUSTERS] = { 0 }; + + switch (indomp->serial) { + case FILESYS_INDOM: need_refresh[CLUSTER_FILESYS]++; break; + case DISK_INDOM: need_refresh[CLUSTER_DISK]++; break; + case CPU_INDOM: need_refresh[CLUSTER_CPU]++; break; + case NETWORK_INDOM: need_refresh[CLUSTER_NETWORK]++; break; + } + darwin_refresh(need_refresh); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +darwin_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i, need_refresh[NUM_CLUSTERS] = { 0 }; + + for (i = 0; i < numpmid; i++) { + __pmID_int *idp = (__pmID_int *)&(pmidlist[i]); + if (idp->cluster >= 0 && idp->cluster < NUM_CLUSTERS) + need_refresh[idp->cluster]++; + } + darwin_refresh(need_refresh); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +void +darwin_init(pmdaInterface *dp) +{ + if (_isDSO) { + int sep = __pmPathSeparator(); + char helppath[MAXPATHLEN]; + sprintf(helppath, "%s%c" "darwin" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_3, "darwin DSO", helppath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.two.instance = darwin_instance; + dp->version.two.fetch = darwin_fetch; + pmdaSetFetchCallBack(dp, darwin_fetchCallBack); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_DIRECT); + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); + + mach_host = mach_host_self(); + host_page_size(mach_host, &mach_page_size); + mach_page_shift = ffs(mach_page_size) - 1; + if (refresh_hertz(&mach_hertz) != 0) + mach_hertz = 100; + init_network(); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n\n", pmProgname); + fputs("Options:\n" +" -d domain use domain (numeric) for metrics domain of PMDA\n" +" -l logfile write log into logfile rather than using default log name\n" +" -U username user account to run under (default \"pcp\")\n" +"\nExactly one of the following options may appear:\n" +" -i port expect PMCD to connect on given inet port (number or name)\n" +" -p expect PMCD to supply stdin/stdout (pipe)\n" +" -u socket expect PMCD to connect on given unix domain socket\n" +" -6 port expect PMCD to connect on given ipv6 port (number or name)\n", + stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int c, sep = __pmPathSeparator(); + int errflag = 0; + char helppath[MAXPATHLEN]; + + _isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + sprintf(helppath, "%s%c" "darwin" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, DARWIN, "darwin.log", + helppath); + + while ((c = pmdaGetOpt(argc, argv, "D:d:i:l:pu:U:6:?", &dispatch, &errflag)) != EOF) { + switch(c) { + case 'U': + username = optarg; + break; + default: + errflag++; + } + } + if (errflag) + usage(); + + pmdaOpenLog(&dispatch); + darwin_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/darwin/pmns b/src/pmdas/darwin/pmns new file mode 100644 index 0000000..da82370 --- /dev/null +++ b/src/pmdas/darwin/pmns @@ -0,0 +1,258 @@ + +hinv { + physmem DARWIN:1:2 + pagesize DARWIN:0:0 + ncpu DARWIN:8:71 + nfilesys DARWIN:5:31 + ndisk DARWIN:7:46 +} + +pmda { + uname DARWIN:2:28 + version DARWIN:2:29 +} + +kernel { + uname + all + percpu +} + +kernel.uname { + release DARWIN:2:23 + version DARWIN:2:24 + sysname DARWIN:2:25 + machine DARWIN:2:26 + nodename DARWIN:2:27 +} + +kernel.all { + cpu + load DARWIN:3:30 + uptime DARWIN:9:76 + hz DARWIN:0:1 +} + +kernel.all.cpu { + user DARWIN:6:42 + nice DARWIN:6:43 + sys DARWIN:6:44 + idle DARWIN:6:45 +} + +kernel.percpu { + cpu +} + +kernel.percpu.cpu { + user DARWIN:8:72 + nice DARWIN:8:73 + sys DARWIN:8:74 + idle DARWIN:8:75 +} + +mem { + physmem DARWIN:1:3 + freemem DARWIN:1:4 + active DARWIN:1:5 + inactive DARWIN:1:6 + pages + pageins DARWIN:1:15 + pageouts DARWIN:1:16 + cache_hits DARWIN:1:17 + cache_lookups DARWIN:1:18 + util +} + +mem.pages { + freemem DARWIN:1:7 + active DARWIN:1:8 + inactive DARWIN:1:9 + reactivated DARWIN:1:10 + wired DARWIN:1:11 + faults DARWIN:1:12 + cow_faults DARWIN:1:13 + zero_filled DARWIN:1:14 +} + +mem.util { + wired DARWIN:1:19 + active DARWIN:1:20 + inactive DARWIN:1:21 + free DARWIN:1:22 +} + +filesys { + capacity DARWIN:5:32 + used DARWIN:5:33 + free DARWIN:5:34 + maxfiles DARWIN:5:129 + usedfiles DARWIN:5:35 + freefiles DARWIN:5:36 + mountdir DARWIN:5:37 + full DARWIN:5:38 + blocksize DARWIN:5:39 + avail DARWIN:5:40 + type DARWIN:5:41 +} + +disk { + dev + all +} + +disk.dev { + read DARWIN:7:47 + write DARWIN:7:48 + total DARWIN:7:49 + read_bytes DARWIN:7:50 + write_bytes DARWIN:7:51 + total_bytes DARWIN:7:52 + blkread DARWIN:7:53 + blkwrite DARWIN:7:54 + blktotal DARWIN:7:55 + read_time DARWIN:7:56 + write_time DARWIN:7:57 + total_time DARWIN:7:58 +} + +disk.all { + read DARWIN:7:59 + write DARWIN:7:60 + total DARWIN:7:61 + read_bytes DARWIN:7:62 + write_bytes DARWIN:7:63 + total_bytes DARWIN:7:64 + blkread DARWIN:7:65 + blkwrite DARWIN:7:66 + blktotal DARWIN:7:67 + read_time DARWIN:7:68 + write_time DARWIN:7:69 + total_time DARWIN:7:70 +} + +network { + interface +} + +network.interface { + in + out + collisions DARWIN:10:86 + mtu DARWIN:10:87 + baudrate DARWIN:10:88 + total +} + +network.interface.in { + bytes DARWIN:10:77 + packets DARWIN:10:78 + errors DARWIN:10:79 + drops DARWIN:10:80 + mcasts DARWIN:10:81 +} + +network.interface.out { + bytes DARWIN:10:82 + packets DARWIN:10:83 + errors DARWIN:10:84 + mcasts DARWIN:10:85 +} + +network.interface.total { + bytes DARWIN:10:89 + packets DARWIN:10:90 + errors DARWIN:10:91 + drops DARWIN:10:92 + mcasts DARWIN:10:93 +} + +nfs3 { + client + server +} + +nfs3.client { + calls DARWIN:11:94 + reqs DARWIN:11:95 +} + +nfs3.server { + calls DARWIN:11:96 + reqs DARWIN:11:97 +} + +rpc { + client + server +} + +rpc.client { + rpccnt DARWIN:11:98 + rpcretrans DARWIN:11:99 + rpctimeouts DARWIN:11:100 + rpcinvalid DARWIN:11:101 + rpcunexpected DARWIN:11:102 + attrcache + lookupcache + biocache + direofcache +} + +rpc.client.attrcache { + hits DARWIN:11:103 + misses DARWIN:11:104 +} + +rpc.client.lookupcache { + hits DARWIN:11:105 + misses DARWIN:11:106 +} + +rpc.client.biocache { + read + write + readlink + readdir +} + +rpc.client.biocache.read { + hits DARWIN:11:107 + misses DARWIN:11:108 +} + +rpc.client.biocache.write { + hits DARWIN:11:109 + misses DARWIN:11:110 +} + +rpc.client.biocache.readlink { + hits DARWIN:11:111 + misses DARWIN:11:112 +} + +rpc.client.biocache.readdir { + hits DARWIN:11:113 + misses DARWIN:11:114 +} + +rpc.client.direofcache { + hits DARWIN:11:115 + misses DARWIN:11:116 +} + +rpc.server { + retfailed DARWIN:11:117 + faults DARWIN:11:118 + cache + vopwrites DARWIN:11:126 + pageins DARWIN:11:127 + pageouts DARWIN:11:128 +} + +rpc.server.cache { + inprog DARWIN:11:119 + nonidem DARWIN:11:120 + idem DARWIN:11:121 + misses DARWIN:11:122 +} diff --git a/src/pmdas/darwin/root b/src/pmdas/darwin/root new file mode 100644 index 0000000..4a4ad71 --- /dev/null +++ b/src/pmdas/darwin/root @@ -0,0 +1,15 @@ +#include + +root { + kernel + pmda + mem + hinv + filesys + disk + network + nfs3 + rpc +} + +#include "pmns" diff --git a/src/pmdas/dbping/GNUmakefile b/src/pmdas/dbping/GNUmakefile new file mode 100644 index 0000000..43044e3 --- /dev/null +++ b/src/pmdas/dbping/GNUmakefile @@ -0,0 +1,56 @@ +#!gmake +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = dbping +DOMAIN = DBPING +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove dbprobe.pl pmda$(IAM).pl + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) dbprobe.$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) +dbprobe.1: dbprobe.pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl dbprobe.pl $(PMDADIR) + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/dbping/Install b/src/pmdas/dbping/Install new file mode 100755 index 0000000..09f5432 --- /dev/null +++ b/src/pmdas/dbping/Install @@ -0,0 +1,36 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Database Ping PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dbping +dso_opt=false +perl_opt=true +daemon_opt=false +forced_restart=false + +perl -e "use DBI" 2>/dev/null +if test $? -ne 0; then + echo "Perl database interface (DBI) is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall + +exit 0 diff --git a/src/pmdas/dbping/Remove b/src/pmdas/dbping/Remove new file mode 100755 index 0000000..7924339 --- /dev/null +++ b/src/pmdas/dbping/Remove @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the Database Ping PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dbping +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/dbping/dbprobe.pl b/src/pmdas/dbping/dbprobe.pl new file mode 100644 index 0000000..5f822dc --- /dev/null +++ b/src/pmdas/dbping/dbprobe.pl @@ -0,0 +1,93 @@ +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +use strict; +use warnings; +use DBI; +use PCP::PMDA qw(pmda_config); +use Time::HiRes qw(gettimeofday); + +my $database = 'DBI:mysql:mysql'; +my $username = 'dbmonitor'; +my $password = 'dbmonitor'; +my $response = 'SELECT 1'; +my $delay = $ARGV[0]; # delay in seconds between database ping's +$delay = 60 unless defined($delay); + +# Configuration files for overriding the above settings +for my $file ( '/etc/pcpdbi.conf', # system defaults (lowest priority) + pmda_config('PCP_PMDAS_DIR') . '/dbping/dbprobe.conf', + './dbprobe.conf' ) { # current directory (high priority) + eval `cat $file` unless ! -f $file; +} + +use vars qw( $pmda $dbh $sth ); + +sub dbping { # must return array of (response_time, status, time_stamp) + my $before; + + if (!defined($dbh)) { # reconnect if necessary + $dbh = DBI->connect($database, $username, $password, undef) || return; + $pmda->log("Connected to database.\n"); + undef $sth; + } + if (!defined($sth)) { # prepare SQL statement once only + $sth = $dbh->prepare($response) || return; + } + $before = gettimeofday; + $sth->execute || return; + while (my @row = $sth->fetchrow_array) { + ($sth->err) && return; + } + + my $timenow = localtime; + print "$timenow\t", gettimeofday - $before, "\n"; +} + +$dbh = DBI->connect($database, $username, $password, undef) || + $pmda->log("Failed initial connect: $dbh->errstr\n"); + +$| = 1; # IMPORTANT! Enables auto-flush, for piping hot pipes. +for (;;) { + dbping; + sleep($delay); +} + +=pod + +=head1 NAME + +dbprobe.pl - database response time and availability information + +=head1 SYNOPSIS + +dbprobe.pl [ delay ] + +=head1 DESCRIPTION + +The B utility is used by pmdadbping(1) to measure +response time from a database. A given query is executed on +the database at the requested interval (I, which defaults +to 60 seconds). This response time measure can be exported +via the Performance Co-Pilot framework for live and historical +monitoring using pmdadbping(1). + +=head1 SEE ALSO + +pmcd(1), pmdadbping(1) and DBI(3). diff --git a/src/pmdas/dbping/pmdadbping.pl b/src/pmdas/dbping/pmdadbping.pl new file mode 100644 index 0000000..ec8f2a0 --- /dev/null +++ b/src/pmdas/dbping/pmdadbping.pl @@ -0,0 +1,192 @@ +# +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +use strict; +use warnings; +use PCP::PMDA; +use vars qw( $pmda $stamp $response $status $timestamp ); + +my $delay = $ARGV[0]; # delay in seconds between database ping's +$delay = 60 unless defined($delay); +my $dbprobe = pmda_config('PCP_PMDAS_DIR') . '/dbping/dbprobe.pl'; +$dbprobe = "perl " . $dbprobe . " $delay"; +my ( $stamp, $response, $status, $timestamp ) = ( 0, 0, 1, 0 ); + +sub dbping_probe_callback +{ + ( $_ ) = @_; + ($stamp, $response) = split(/\t/); + + # $pmda->log("dbping_probe_callback: time=$stamp resp=$response\n"); + + if (defined($stamp) && defined($response)) { + $timestamp = $stamp; + $status = 0; + } else { + $response = -1; + $status = 1; # bad result, keep old $timestamp + } +} + +sub dbping_fetch_callback # must return array of value,status +{ + my ($cluster, $item, $inst) = @_; + + # $pmda->log("dbping_fetch_callback $cluster:$item ($inst)\n"); + + return (PM_ERR_INST, 0) unless ($inst == PM_IN_NULL); + + if ($cluster == 0) { + if ($item == 0) { return ($response, 1); } + elsif ($item == 1) { return ($status, 1); } + } + elsif ($cluster == 1) { + if ($item == 0) { return ($timestamp, 1); } + elsif ($item == 1) { return ($delay, 1); } + } + return (PM_ERR_PMID, 0); +} + +sub dbping_store_callback # must return a single value (scalar context) +{ + my ($cluster, $item, $inst, $val) = @_; + my $sts = 0; + + # $pmda->log("dbping_store_callback $cluster:$item ($inst) $val\n"); + + if ($cluster == 1 && $item == 1) { + $delay = $val; + return 0; + } + elsif ( ($cluster == 0 && ($item == 0 || $item == 1)) + || ($cluster == 1 && $item == 0) ) { + return PM_ERR_PERMISSION; + } + return PM_ERR_PMID; +} + +$pmda = PCP::PMDA->new('dbping', 244); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_DOUBLE, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dbping.response_time', + 'Length of time taken to access the database', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'dbping.status', + 'Success state of last attempt to ping the database', ''); +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'dbping.control.timestamp', + 'Time of last successful database ping', ''); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dbping.control.delay', + 'Time to sleep between database ping attempts', ''); +$pmda->set_fetch_callback( \&dbping_fetch_callback ); +$pmda->set_store_callback( \&dbping_store_callback ); +$pmda->add_pipe( $dbprobe, \&dbping_probe_callback, 0 ); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdadbping - database response time and availability PMDA + +=head1 DESCRIPTION + +Simple database response time measurement PMDA. dbprobe.pl(1) +should be configured to use the type of DBI appropriate +for your database, which includes: RDBMS flavour, user/password, +delay between "ping" requests, and the SQL statement to use. +B runs dbprobe.pl(1), and exports the performance +measurements it makes available as PCP metrics. + +=head1 INSTALLATION + +Configure dbprobe.pl(1) - it uses a configuration file from +(in this order): + +=over + +=item * /etc/pcpdbi.conf + +=item * $PCP_PMDAS_DIR/dbping/dbprobe.conf + +=back + +This file can contain overridden values (Perl code) for the settings +listed at the start of dbprobe.pl, namely: + +=over + +=item * database name (see DBI(3) for details) + +=item * database user name + +=item * database pass word + +=item * SQL statement to measure (probe) + +=item * delay between probes + +=back + +Once this is setup, you can access the names and values for the +dbping performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/dbping + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/dbping + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/dbping/probes.stp + +probe configuration file for stap(1), run by B + +=item $PCP_PMDAS_DIR/dbping/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/dbping/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/dbping.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), dbprobe.pl(1) and DBI(3). diff --git a/src/pmdas/dmcache/GNUmakefile b/src/pmdas/dmcache/GNUmakefile new file mode 100644 index 0000000..243f08f --- /dev/null +++ b/src/pmdas/dmcache/GNUmakefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = dmcache +PYSCRIPT = pmda$(IAM).python +LSRCFILES = Install Remove $(PYSCRIPT) + +DOMAIN = DMCACHE +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log + +default_pcp default: check_domain + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(PYSCRIPT) $(PMDADIR)/$(PYSCRIPT) + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PYTHONRULE) diff --git a/src/pmdas/dmcache/Install b/src/pmdas/dmcache/Install new file mode 100644 index 0000000..64ef3fa --- /dev/null +++ b/src/pmdas/dmcache/Install @@ -0,0 +1,35 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Device Mapper Cache PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dmcache +python_opt=true +daemon_opt=false +forced_restart=true + +which dmsetup >/dev/null 2>&1 +if [ $? -ne 0 ] +then + echo "Device Mapper 'dmsetup' binary not found, cannot proceed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/dmcache/Remove b/src/pmdas/dmcache/Remove new file mode 100644 index 0000000..6586d55 --- /dev/null +++ b/src/pmdas/dmcache/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Device Mapper Cache PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dmcache + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/dmcache/pmdadmcache.python b/src/pmdas/dmcache/pmdadmcache.python new file mode 100644 index 0000000..35db1a0 --- /dev/null +++ b/src/pmdas/dmcache/pmdadmcache.python @@ -0,0 +1,264 @@ +''' +Performance Metrics Domain Agent exporting Device Mapper Cache metrics. +''' +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +from pcp.pmda import PMDA, pmdaMetric, pmdaIndom, pmdaUnits +from ctypes import POINTER, Structure, cast, c_ulonglong, c_uint +from cpmapi import (PM_SEM_DISCRETE, PM_SEM_INSTANT, PM_SEM_COUNTER, + PM_ERR_INST, PM_ERR_PMID, PM_SPACE_BYTE, PM_SPACE_KBYTE, + PM_TYPE_U32, PM_TYPE_U64, PM_TYPE_STRING, PM_COUNT_ONE) +from subprocess import PIPE, Popen +from os import getenv + +IOMODES = { 'writeback': 1, 'writethrough': 2, 'passthrough': 3 } + +class DmCacheStats(Structure): + ''' Metric values associated with each dm-cache target ''' + _fields_ = [("size", c_ulonglong), + ("metadata_block_size", c_uint), + ("metadata_used", c_ulonglong), + ("metadata_total", c_ulonglong), + ("cache_block_size", c_uint), + ("cache_used", c_ulonglong), + ("cache_total", c_ulonglong), + ("read_hits", c_uint), + ("read_misses", c_uint), + ("write_hits", c_uint), + ("write_misses", c_uint), + ("demotions", c_uint), + ("promotions", c_uint), + ("dirty", c_ulonglong), + ("iomode", c_uint)] + + def __init__(self, text): + Structure.__init__(self) + self.parse(text) + + def parse(self, line): + ''' + Parse an individual line of dmcache statistics text. The format is: + : [Values...] + Values are as in Documentation/device-mapper/cache.txt Status section: + <#used metadata blocks>/<#total metadata blocks> + <#used cache blocks>/<#total cache blocks> + <#read hits> <#read misses> <#write hits> <#write misses> + <#demotions> <#promotions> <#dirty> <#features> * + ''' + data = line.replace('/', ' ').split(' ') + if len(data) < 19: + raise ValueError(line) + mbsize = long(data[4]) * 512 # metadata block size (from sectors) + cbsize = long(data[7]) * 512 # cachedev block size (from sectors) + + self.size = long(data[2]) - long(data[1]) + self.metadata_block_size = mbsize + self.metadata_used = long(data[5]) * mbsize / 1024 + self.metadata_total = long(data[6]) * mbsize / 1024 + self.cache_block_size = cbsize + self.cache_used = long(data[8]) * cbsize / 1024 + self.cache_total = long(data[9]) * cbsize / 1024 + self.read_hits = long(data[10]) + self.read_misses = long(data[11]) + self.write_hits = long(data[12]) + self.write_misses = long(data[13]) + self.demotions = long(data[14]) + self.promotions = long(data[15]) + self.dirty = long(data[16]) * cbsize / 1024 + self.iomode = IOMODES[data[18]] + + def io_mode(self): + ''' Human-readable representation of numeric io_mode encoding ''' + for mode in IOMODES: + if self.iomode == IOMODES[mode]: + return mode + return 'unknown' + +class DmCachePMDA(PMDA): + ''' + Performance Metrics Domain Agent exporting dm-cache module metrics. + Install and make basic use of it, if you use dm-cache, as follows: + # $PCP_PMDAS_DIR/dmcache/Install + $ pminfo -fmdtT dmcache + ''' + + def __init__(self, name, domain): + ''' + Setup metric table (see drivers/md/dm-cache-target.c::cache_status), + the cache devices instance domain, and our callback routines. + ''' + PMDA.__init__(self, name, domain) + + # dm-cache devices instance domain + self.caches = {} + self.dmstatus = getenv('DM_STATUS', 'dmsetup status --target=cache') + + self.cache_indom = self.indom(0) + self.add_indom(pmdaIndom(self.cache_indom, self.caches)) + + self.set_fetch(self.dmcache_refresh) + self.set_fetch_callback(self.dmcache_fetch_callback) + + self.add_metric('dmcache.size', + pmdaMetric(self.pmid(0, 0), + PM_TYPE_U64, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Total size of each cache device (Kbytes)') + + self.add_metric('dmcache.metadata.block_size', + pmdaMetric(self.pmid(0, 1), + PM_TYPE_U32, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_BYTE, 0, 0)), + 'Fixed block size for each metadata block in bytes') + self.add_metric('dmcache.metadata.used', + pmdaMetric(self.pmid(0, 2), + PM_TYPE_U64, self.cache_indom, PM_SEM_INSTANT, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Number of metadata blocks used') + self.add_metric('dmcache.metadata.total', + pmdaMetric(self.pmid(0, 3), + PM_TYPE_U64, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Total number of metadata blocks available') + + self.add_metric('dmcache.cache.block_size', + pmdaMetric(self.pmid(0, 4), + PM_TYPE_U32, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_BYTE, 0, 0)), + 'Fixed block size for each metadata block in bytes') + self.add_metric('dmcache.cache.used', + pmdaMetric(self.pmid(0, 5), + PM_TYPE_U64, self.cache_indom, PM_SEM_INSTANT, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Number of cache blocks used') + self.add_metric('dmcache.cache.total', + pmdaMetric(self.pmid(0, 6), + PM_TYPE_U64, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Total number of cache blocks available') + + self.add_metric('dmcache.read_hits', + pmdaMetric(self.pmid(0, 7), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a READ bio has been mapped to the cache') + self.add_metric('dmcache.read_misses', + pmdaMetric(self.pmid(0, 8), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a READ bio has been mapped to the origin') + self.add_metric('dmcache.write_hits', + pmdaMetric(self.pmid(0, 9), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a WRITE bio has been mapped to the cache') + self.add_metric('dmcache.write_misses', + pmdaMetric(self.pmid(0, 10), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a WRITE bio has been mapped to the origin') + + self.add_metric('dmcache.demotions', + pmdaMetric(self.pmid(0, 11), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a block has been removed from the cache') + self.add_metric('dmcache.promotions', + pmdaMetric(self.pmid(0, 12), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a block has been moved to the cache') + + self.add_metric('dmcache.dirty', + pmdaMetric(self.pmid(0, 13), + PM_TYPE_U64, self.cache_indom, PM_SEM_INSTANT, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Number of blocks in the cache that differ from the origin') + self.add_metric('dmcache.io_mode_code', + pmdaMetric(self.pmid(0, 14), + PM_TYPE_U32, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(0, 0, 0, 0, 0, 0)), + 'Cache mode code - writeback, writethrough or passthrough') + self.add_metric('dmcache.io_mode', + pmdaMetric(self.pmid(1, 0), + PM_TYPE_STRING, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(0, 0, 0, 0, 0, 0)), + 'Cache mode string - writeback, writethrough or passthrough') + + def dmcache_refresh(self): + ''' Refresh the values and instances for all device mapper caches ''' + pipe = Popen(self.dmstatus, shell = True, stdout = PIPE, stderr = PIPE) + output, errors = pipe.communicate() + if errors: + self.err("dmcache_refresh: %s error: %s" % (self.dmstatus, errors)) + + self.caches.clear() + for line in output.splitlines(): + if 'No devices found' in line: + continue + name = line[:line.find(':')] # extract cache name + self.caches[name] = DmCacheStats(line) # extract stats values + self.replace_indom(self.cache_indom, self.caches) + + def dmcache_fetch_callback(self, cluster, item, inst): + ''' + Look up value associated with requested instance (inst) of a + given metric (cluster:item) PMID, returning a [value,status] + ''' + value = self.inst_lookup(self.cache_indom, inst) + if (value == None): + return [PM_ERR_INST, 0] + value = cast(value, POINTER(DmCacheStats)) + cache = value.contents + + if cluster == 1 and item == 0: + return [cache.io_mode(), 1] # human-readable string encoding + elif cluster != 0: + return [PM_ERR_PMID, 0] + + if item == 0: + return [cache.size, 1] + elif item == 1: + return [cache.metadata_block_size, 1] + elif item == 2: + return [cache.metadata_used, 1] + elif item == 3: + return [cache.metadata_total, 1] + elif item == 4: + return [cache.cache_block_size, 1] + elif item == 5: + return [cache.cache_used, 1] + elif item == 6: + return [cache.cache_total, 1] + elif item == 7: + return [cache.read_hits, 1] + elif item == 8: + return [cache.read_misses, 1] + elif item == 9: + return [cache.write_hits, 1] + elif item == 10: + return [cache.write_misses, 1] + elif item == 11: + return [cache.demotions, 1] + elif item == 12: + return [cache.promotions, 1] + elif item == 13: + return [cache.dirty, 1] + elif item == 14: + return [cache.iomode, 1] + return [PM_ERR_PMID, 0] + +if __name__ == '__main__': + DmCachePMDA('dmcache', 129).run() diff --git a/src/pmdas/dtsrun/GNUmakefile b/src/pmdas/dtsrun/GNUmakefile new file mode 100644 index 0000000..496567b --- /dev/null +++ b/src/pmdas/dtsrun/GNUmakefile @@ -0,0 +1,52 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = dtsrun +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "mingw" +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) +else +install: +endif + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/dtsrun/Install b/src/pmdas/dtsrun/Install new file mode 100755 index 0000000..c7cc772 --- /dev/null +++ b/src/pmdas/dtsrun/Install @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the DTS (Data Transformation Services) dtsrun log file PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dtsrun +perl_opt=true +daemon_opt=false +forced_restart=false + +pmdaSetup + +if $do_pmda +then + while true + do + echo + $PCP_ECHO_PROG $PCP_ECHO_N "DTS log filename? ""$PCP_ECHO_C" + read logfile + [ ! -z "$logfile" ] && break + echo + done + args="$args logfile" +fi + +pmdaInstall +exit 0 diff --git a/src/pmdas/dtsrun/Remove b/src/pmdas/dtsrun/Remove new file mode 100755 index 0000000..aa878af --- /dev/null +++ b/src/pmdas/dtsrun/Remove @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the DTS (Data Transformation Services) dtsrun log PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dtsrun + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/dtsrun/pmdadtsrun.pl b/src/pmdas/dtsrun/pmdadtsrun.pl new file mode 100644 index 0000000..9d09d6e --- /dev/null +++ b/src/pmdas/dtsrun/pmdadtsrun.pl @@ -0,0 +1,341 @@ +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use Time::Local; + +my $pmda = PCP::PMDA->new('dtsrun', 102); + +my ( $package, $package_starttime, $instance_refresh ); +my $package_indom = 0; # instance domain for DTS package +my @package_instances = (); +my ( $step, $step_starttime ); +my $steps_indom = 1; # instance domain for DTS package steps +my @steps_instances = (); + +use vars qw ( %lastpkg_times %lastpkg_stamp %allpkgs_times %allpkgs_count + %laststep_times %laststep_stamp %laststep_offset + %laststep_status %laststep_message %allsteps_times + %allsteps_count %allsteps_errors %allpkgs_delta + %packages %steps + ); + +sub parsetime +{ + shift; + #$pmda->log("parsetime got dtsrun timestamp: $_"); + m|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+) (\w+)|; + my ($mm, $dd, $year, $hr, $min, $sec, $ampm) = ($1,$2,$3,$4,$5,$6,$7); + $hr += 12 unless ($ampm eq 'AM'); + $hr -= 1; # range 0..23 (hours) + $mm -= 1; # range 0..11 (months) + my $tm = timelocal($sec,$min,$hr,$dd,$mm,$year); + return $tm; +} + +sub dtsrun_parser +{ + ( undef, $_ ) = @_; + + s/\r//g; # cull Windows line endings + #$pmda->log("dtsrun_parser got line: $_"); + + if (/^Step '(\S+)' (.*)/) { + $step = "$package/$1"; + if (defined($steps{$step})) { + $allsteps_count{$step}++; + } else { # new step instance + $steps{$step} = 1; + $allsteps_times{$step} = 0; + $allsteps_count{$step} = 1; + $allsteps_errors{$step} = 0; + $laststep_times{$step} = 0; + $laststep_stamp{$step} = ''; + $laststep_offset{$step} = 0; + $laststep_status{$step} = -1; + $laststep_message{$step} = ''; + $instance_refresh = 1; + } + if ($2 eq 'succeeded') { + $laststep_status{$step} = 0; + } else { + $laststep_status{$step} = -1; + $allsteps_errors{$step}++; + } + $laststep_message{$step} = $2; + } + elsif (/^Step Execution Started: (.*)/) { + $step_starttime = parsetime($1); + $laststep_stamp{$step} = $1; + $laststep_offset{$step} = $step_starttime - $package_starttime; + } + elsif (/^Total Step Execution Time: (\S+) seconds/) { + $laststep_times{$step} = $1; + $allsteps_times{$step} += $1; + } + elsif (/^Package Name: (\w+)/) { + $package = $1; + if (defined($packages{$package})) { + $allpkgs_count{$package}++; + } else { # new package instance + $packages{$package} = 1; + $allpkgs_delta{$package} = 0; + $allpkgs_times{$package} = 0; + $allpkgs_count{$package} = 1; + $lastpkg_times{$package} = 0; + $lastpkg_stamp{$package} = ''; + $instance_refresh = 1; + } + } + elsif (/^Execution Started: (.*)/) { + my $latest = parsetime($1); + if (defined($package_starttime) && $package_starttime >= 0) { + $allpkgs_delta{$package} = $latest - $package_starttime; + } else { + $allpkgs_delta{$package} = -1; + } + $package_starttime = $latest; + $lastpkg_stamp{$package} = $1; + } + elsif (/^Total Execution Time: (\S+) seconds/) { + $lastpkg_times{$package} = $1; + $allpkgs_times{$package} += $1; + } + elsif (/^\*\*\*/) { + # end section, cleanup global state + undef $step; + undef $package; + } +} + +sub dtsrun_instance +{ + my ( $pkgcount, $stepcount ); + + #$pmda->log("dtsrun_instance"); + return unless (defined($instance_refresh) && $instance_refresh); + + @package_instances = (); + @steps_instances = (); + + $pkgcount = $stepcount = 0; + foreach my $pkgname (keys(%packages)) { + push @package_instances, $pkgcount++, $pkgname; + foreach my $stepname (sort keys(%steps)) { + push @steps_instances, $stepcount++, $stepname; + } + } + + #$pmda->log("dtsrun_instance: $pkgcount packages, $stepcount steps"); + $pmda->replace_indom($package_indom, \@package_instances); + $pmda->replace_indom($steps_indom, \@steps_instances); + $instance_refresh = 0; +} + +sub dtsrun_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $instance; + + #$pmda->log("dtsrun_fetch_callback for PMID: $cluster.$item ($inst)"); + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + + if ($cluster == 0 || $cluster == 1 || $cluster == 4) { + $instance = pmda_inst_name($package_indom, $inst); + } else { + $instance = pmda_inst_name($steps_indom, $inst); + } + if (!defined($instance)) { return (PM_ERR_INST, 0); } + #$pmda->log("dtsrun_fetch_callback using instance $instance"); + + if ($cluster == 0) { + # dtsrun.package.last.time + if ($item == 0) { return ($lastpkg_times{$instance}, 1); } + # dtsrun.package.last.timestamp + elsif ($item == 1) { return ($lastpkg_stamp{$instance}, 1); } + } + elsif ($cluster == 1) { + # dtsrun.package.total.time + if ($item == 0) { return ($allpkgs_times{$instance}, 1); } + # dtsrun.package.total.count + elsif ($item == 1) { return ($allpkgs_count{$instance}, 1); } + } + elsif ($cluster == 2) { + # dtsrun.package.steps.last.time + if ($item == 0) { return ($laststep_times{$instance}, 1); } + # dtsrun.package.steps.last.timestamp + if ($item == 1) { return ($laststep_stamp{$instance}, 1); } + # dtsrun.package.steps.last.offset + if ($item == 2) { return ($laststep_offset{$instance}, 1); } + # dtsrun.package.steps.last.status + if ($item == 3) { return ($laststep_status{$instance}, 1); } + # dtsrun.package.steps.last.message + if ($item == 4) { return ($laststep_message{$instance}, 1); } + } + elsif ($cluster == 3) { + # dtsrun.package.steps.total.time + if ($item == 0) { return ($allsteps_times{$instance}, 1); } + # dtsrun.package.steps.total.count + if ($item == 1) { return ($allsteps_count{$instance}, 1); } + # dtsrun.package.steps.total.errors + if ($item == 2) { return ($allsteps_errors{$instance}, 1); } + } + elsif ($cluster == 4) { + # dtsrun.package.interval + if ($item == 0) { return ($allpkgs_delta{$instance}, 1); } + } + return (PM_ERR_PMID, 0); +} + +sub dtsrun_setup_metrics +{ + $pmda->add_metric(pmda_pmid(0,0), PM_TYPE_FLOAT, $package_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dtsrun.package.lastrun.time', '', + 'Time taken for DTS package to complete the last time it was run'); + $pmda->add_metric(pmda_pmid(0,1), PM_TYPE_STRING, $package_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'dtsrun.package.lastrun.timestamp', '', + 'Time stamp (string) of the last package run for each DTS package'); + + $pmda->add_metric(pmda_pmid(1,0), PM_TYPE_FLOAT, $package_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dtsrun.package.total.time', '', + 'Cumulative time taken to date for all DTS package runs.'); + $pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U32, $package_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'dtsrun.package.total.count', '', + 'Cumulative count of all DTS package runs to date.'); + + $pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $steps_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dtsrun.package.steps.last.time', '', + 'Time taken to complete the last iteration of each step.'); + $pmda->add_metric(pmda_pmid(2,1), PM_TYPE_STRING, $steps_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'dtsrun.package.steps.last.timestamp', '', + 'Time of completion of the last iteration of each step.'); + $pmda->add_metric(pmda_pmid(2,2), PM_TYPE_U32, $steps_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dtsrun.package.steps.last.offset', '', + 'Offset from package start time for this step, in seconds.'); + $pmda->add_metric(pmda_pmid(2,3), PM_TYPE_32, $steps_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'dtsrun.package.steps.last.status', '', + 'Indicator of success (0) or failure of last run for each step.'); + $pmda->add_metric(pmda_pmid(2,4), PM_TYPE_STRING, $steps_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'dtsrun.package.steps.last.message', '', + 'Message string used to determine status of last run for each step.'); + + $pmda->add_metric(pmda_pmid(3,0), PM_TYPE_FLOAT, $steps_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dtsrun.package.steps.total.time', '', + 'Cumulative count of time taken for runs of each DTS package step.'); + $pmda->add_metric(pmda_pmid(3,1), PM_TYPE_U32, $steps_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'dtsrun.package.steps.total.count', '', + 'Cumulative count of runs of each DTS package step.'); + $pmda->add_metric(pmda_pmid(3,2), PM_TYPE_U32, $steps_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'dtsrun.package.steps.total.errors', '', + 'Cumulative count of errors observed for each DTS package step.'); + + $pmda->add_metric(pmda_pmid(4,0), PM_TYPE_32, $package_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'dtsrun.package.interval', '', + 'Time between consecutive observations of each DTS package run. Unknown is -1.'); +} + +sub dtsrun_setup_instances +{ + $package_indom = $pmda->add_indom($package_indom, [], '', ''); + $steps_indom = $pmda->add_indom($steps_indom, [], '', ''); +} + +my $logfile = ''; +if (!defined($ENV{PCP_PERL_PMNS} && !defined($ENV{PCP_PERL_DOMAIN}))) { + die "No dtsrun statistics file specified\n" unless (defined($ARGV[0])); + $logfile = $ARGV[0]; + die "Cannot find a valid dtsrun statistics file\n" unless -f $logfile; +} +$pmda->log("Using logfile: $logfile"); + +dtsrun_setup_metrics; +dtsrun_setup_instances; + +$pmda->add_tail($logfile, \&dtsrun_parser, 0); +$pmda->set_fetch(\&dtsrun_instance); +$pmda->set_instance(\&dtsrun_instance); +$pmda->set_fetch_callback(\&dtsrun_fetch_callback); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdadtsrun - Data Transformation Services process (dtsrun) log file PMDA + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +exports metric values from the DTS run executable, which is a data +transformation server, used with SQL Server. +Further details on DTS can be found at http://www.dtssql.com/. + +=head1 INSTALLATION + +If you want access to the names and values for the dtsrun performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/dtsrun + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/dtsrun + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item /var/log/dtsrun.log + +log file showing status and elapsed times exported from dtsrun + +=item $PCP_PMDAS_DIR/dtsrun/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/dtsrun/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/dtsrun.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/elasticsearch/GNUmakefile b/src/pmdas/elasticsearch/GNUmakefile new file mode 100644 index 0000000..0a6ae50 --- /dev/null +++ b/src/pmdas/elasticsearch/GNUmakefile @@ -0,0 +1,54 @@ +#!gmake +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = elasticsearch +DOMAIN = ELASTICSEARCH +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/elasticsearch/Install b/src/pmdas/elasticsearch/Install new file mode 100755 index 0000000..ece2b20 --- /dev/null +++ b/src/pmdas/elasticsearch/Install @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Copyright (c) 2011-2012 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the ElasticSearch PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=elasticsearch +perl_opt=true +daemon_opt=false +forced_restart=false + +for module in JSON LWP::UserAgent +do + perl -e "use $module" 2>/dev/null + if test $? -ne 0 + then + echo "$module perl module is not installed" + exit 1 + fi +done + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/elasticsearch/Remove b/src/pmdas/elasticsearch/Remove new file mode 100755 index 0000000..ee6fc31 --- /dev/null +++ b/src/pmdas/elasticsearch/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2011-2012 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the ElasticSearch PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=elasticsearch + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/elasticsearch/pmdaelasticsearch.pl b/src/pmdas/elasticsearch/pmdaelasticsearch.pl new file mode 100755 index 0000000..21f2eda --- /dev/null +++ b/src/pmdas/elasticsearch/pmdaelasticsearch.pl @@ -0,0 +1,882 @@ +# +# Copyright (c) 2011-2013 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use JSON; +use PCP::PMDA; +use LWP::UserAgent; + +my $es_port = 9200; +my $es_instance = 'localhost'; +use vars qw($pmda $http $es_cluster $es_nodes $es_nodestats $es_root $es_searchstats $es_cluster_state); + +my $nodes_indom = 0; +my @nodes_instances; +my @nodes_instance_ids; +my $search_indom = 1; +my @search_instances; +my @search_instance_ids; +my $index_indom = 2; +my @index_instances; +my @index_instance_ids; + +my @cluster_cache; # time of last refresh for each cluster +my $cache_interval = 2; # min secs between refreshes for clusters +my $http_timeout = 1; # max secs for a request (*must* be small). + +# Configuration files for overriding the above settings +for my $file (pmda_config('PCP_PMDAS_DIR') . '/elasticsearch/es.conf', 'es.conf') { + eval `cat $file` unless ! -f $file; +} +my $baseurl = "http://$es_instance:$es_port/"; + +my $http = LWP::UserAgent->new; +$http->agent('pmdaelasticsearch'); +$http->timeout($http_timeout); # if elasticsearch not timely, no soup for you + +# http GET of elasticsearch json from a given url +sub es_agent_get +{ + my $response = $http->get(shift); + return undef unless $response->is_success; + return $response->decoded_content; +} + +# crack json data structure, extract only data-node names +sub es_data_node_instances +{ + my $nodeIDs = shift; + my $i = 0; + + @nodes_instances = (); + @nodes_instance_ids = (); + foreach my $node (keys %$nodeIDs) { + my $attributes = $nodeIDs->{$node}->{'attributes'}; + unless (defined($attributes) && $attributes->{'data'} == 'false') { + my $name = $nodeIDs->{$node}->{'name'}; + $nodes_instances[$i*2] = $i; + $nodes_instances[($i*2)+1] = $name; + $nodes_instance_ids[$i*2] = $i; + $nodes_instance_ids[($i*2)+1] = $node; + $i++; + # $pmda->log("es_instances added node: $name ($node)"); + } + } + $pmda->replace_indom($nodes_indom, \@nodes_instances); +} + +sub es_data_index_instances +{ + my $indexIDs = shift; + my $i = 0; + + @index_instances = (); + @index_instance_ids = (); + foreach my $index (keys %$indexIDs){ + $index_instances[$i*2] = $i; + $index_instances[($i*2)+1] = $index; + $index_instance_ids[$i*2] = $i; + $index_instance_ids[($i*2)+1] = $index; + $i++; + } + $pmda->replace_indom($index_indom, \@index_instances); + +} + +# crack json data structure, extract index names +sub es_search_instances +{ + my $searchIDs = shift; + my $i = 0; + + @search_instances = (); + @search_instance_ids = (); + foreach my $search (keys %$searchIDs) { + $search_instances[$i*2] = $i; + $search_instances[($i*2)+1] = $search; + $search_instance_ids[$i*2] = $i; + $search_instance_ids[($i*2)+1] = $search; + $i++; + # $pmda->log("es_search_instances added index: $search"); + } + $pmda->replace_indom($search_indom, \@search_instances); +} + +sub es_refresh_cluster_health +{ + my $content = es_agent_get($baseurl . "_cluster/health"); + $es_cluster = defined($content) ? decode_json($content) : undef; +} + +# Update the JSON hash of ES indices so we can later map the metric names +# much more easily back to the PMID (during the fetch callback routine). +# +sub es_rewrite_cluster_state +{ + my $indices = $es_cluster_state->{'metadata'}->{'indices'}; + foreach my $index_key (keys %$indices) { + # Go over each setting key and transpose what the key name is called + my $settings = $indices->{$index_key}->{'settings'}; + foreach my $settings_key (keys %$settings) { + # Convert keys like "index.version.created" to "version_created" + my $transformed_key = $settings_key; + $transformed_key =~ s/index\.//; + $transformed_key =~ s/\./_/g; + $settings->{$transformed_key} = $settings->{$settings_key}; + } + } +} + +sub es_refresh_cluster_state +{ + my $content = es_agent_get($baseurl . "_cluster/state"); + if (defined($content)) { + $es_cluster_state = decode_json($content); + es_rewrite_cluster_state(); + es_data_index_instances($es_cluster_state->{'metadata'}->{'indices'}); + } else { + $es_cluster_state = undef; + } +} + +sub es_refresh_cluster_nodes_stats_all +{ + my $content = es_agent_get($baseurl . "_cluster/nodes/stats?all"); + if (defined($content)) { + $es_nodestats = decode_json($content); + es_data_node_instances($es_nodestats->{'nodes'}); + } else { + $es_nodestats = undef; + } +} + +sub es_refresh_cluster_nodes_all +{ + my $content = es_agent_get($baseurl . "_cluster/nodes?all"); + if (defined($content)) { + $es_nodes = decode_json($content); + es_data_node_instances($es_nodes->{'nodes'}); + } else { + $es_nodes = undef; + } +} + +sub es_refresh_root +{ + my $content = es_agent_get($baseurl); + $es_root = defined($content) ? decode_json($content) : undef; +} + +sub es_refresh_stats_search +{ + my $content = es_agent_get($baseurl . "_stats/search"); + if (defined($content)) { + $es_searchstats = decode_json($content); + es_search_instances($es_searchstats->{'_all'}->{'indices'}); + } else { + $es_searchstats = undef; + } +} + +sub es_refresh +{ + my ($cluster) = @_; + my $now = time; + + if (defined($cluster_cache[$cluster]) && + $now - $cluster_cache[$cluster] <= $cache_interval) { + return; + } + + if ($cluster == 0) { # Update the cluster metrics + es_refresh_cluster_health(); + } elsif ($cluster == 1) { # Update the node metrics + es_refresh_cluster_nodes_stats_all(); + } elsif ($cluster == 2) { # Update the other node metrics + es_refresh_cluster_nodes_all(); + } elsif ($cluster == 3) { # Update the root metrics + es_refresh_root(); + } elsif ($cluster == 4 || # Update the search metrics + $cluster == 5) { + es_refresh_stats_search(); + # avoid 2nd refresh call on metrics in other cluster + $cluster_cache[4] = $cluster_cache[5] = $now; + } elsif ($cluster == 6 || $cluster == 7) { # Update the cluster state + es_refresh_cluster_state(); + } + $cluster_cache[$cluster] = $now; +} + +sub es_lookup_node +{ + my ($json, $inst) = @_; + my $nodeID = $nodes_instance_ids[($inst*2)+1]; + return $json->{'nodes'}->{$nodeID}; +} + +sub es_lookup_search +{ + my ($json, $inst) = @_; + my $searchID = $search_instance_ids[($inst*2)+1]; + return $json->{'_all'}->{'indices'}->{$searchID}; +} + +sub es_lookup_index +{ + my ($json, $inst) = @_; + my $indexID = $index_instance_ids[($inst*2)+1]; + return $json->{'metadata'}->{'indices'}->{$indexID}; +} + +# iterate over metric-name components, performing hash lookups as we go. +sub es_value +{ + my ( $values, $names ) = @_; + my ( $value, $name ); + + foreach $name (@$names) { + $value = $values->{$name}; + return (PM_ERR_APPVERSION, 0) unless (defined($value)); + $values = $value; + } + return (PM_ERR_APPVERSION, 0) unless (defined($value)); + return ($value, 1); +} + +# translate status string into a numeric code for pmie and friends +sub es_status +{ + my ($value) = @_; + + if (!defined($value)) { + return (PM_ERR_AGAIN, 0); + } elsif ($value eq "green" || $value eq "false") { + return (0, 1); + } elsif ($value eq "yellow" || $value eq "true") { + return (1, 1); + } elsif ($value eq "red") { + return (2, 1); + } + return (-1, 1); # unknown +} + +sub es_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my @metric_subnames = split(/\./, $metric_name); + my ($node, $json, $search); + + # $pmda->log("es_fetch_callback: $metric_name $cluster.$item ($inst)"); + + if ($cluster == 0) { + if (!defined($es_cluster)) { return (PM_ERR_AGAIN, 0); } + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + + # remove first couple (i.e. elasticsearch.cluster.) + splice(@metric_subnames, 0, 2); + + # cluster.timed_out and cluster.status (numeric codes) + if ($item == 1) { + my $value = $es_cluster->{'status'}; + return (PM_ERR_APPVERSION, 0) unless (defined($value)); + return ($value, 1); + } + elsif ($item == 2 || $item == 10) { + return es_status($es_cluster->{$metric_subnames[0]}); + } + return es_value($es_cluster, \@metric_subnames); + } + elsif ($cluster == 1) { + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + if ($inst > @nodes_instances) { return (PM_ERR_INST, 0); } + + $node = es_lookup_node($es_nodestats, $inst); + if (!defined($node)) { return (PM_ERR_AGAIN, 0); } + + # remove first couple (i.e. elasticsearch.node.) + splice(@metric_subnames, 0, 2); + return es_value($node, \@metric_subnames); + } + elsif ($cluster == 2) { + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + if ($inst > @nodes_instances) { return (PM_ERR_INST, 0); } + + $node = es_lookup_node($es_nodes, $inst); + if (!defined($node)) { return (PM_ERR_AGAIN, 0); } + # remove first couple (i.e. elasticsearch.node.) + splice(@metric_subnames, 0, 2); + return es_value($node, \@metric_subnames); + } + elsif ($cluster == 3) { + if (!defined($es_root)) { return (PM_ERR_AGAIN, 0); } + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + + # remove first one (i.e. elasticsearch.) + splice(@metric_subnames, 0, 1); + return es_value($es_root, \@metric_subnames); + } + elsif ($cluster == 4) { + if (!defined($es_searchstats)) { return (PM_ERR_AGAIN, 0); } + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + + # remove first couple (i.e. elasticsearch.search.) + splice(@metric_subnames, 0, 2); + # regex fixes up _all and _shard for us (invalid names) + $metric_subnames[0] =~ s/^(all|shards)$/_$1/; + return es_value($es_searchstats, \@metric_subnames); + } + elsif ($cluster == 5) { + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + if ($inst > @search_instances) { return (PM_ERR_INST, 0); } + + # remove first three (i.e. elasticsearch.search.perindex.) + splice(@metric_subnames, 0, 3); + $search = es_lookup_search($es_searchstats, $inst); + if (!defined($search)) { return (PM_ERR_AGAIN, 0); } + return es_value($search, \@metric_subnames); + } + elsif ($cluster == 6) { + if (!defined($es_cluster_state)){ return (PM_ERR_AGAIN, 0); } + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + # remove first couple (i.e. elasticsearch.cluster.) + splice(@metric_subnames, 0, 2); + return es_value($es_cluster_state, \@metric_subnames); + } + elsif ($cluster == 7) { + # Remove elasticsearch.index + splice(@metric_subnames, 0, 2); + if (!defined($es_cluster_state)){ return (PM_ERR_AGAIN, 0); } + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + $search = es_lookup_index($es_cluster_state, $inst); + if (!defined($search)) { return (PM_ERR_AGAIN, 0); } + return es_value($search, \@metric_subnames); + + } + return (PM_ERR_PMID, 0); +} + +$pmda = PCP::PMDA->new('elasticsearch', 108); + +# cluster stats +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.cluster_name', + 'Name of the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.status.colour', + 'Status (green,yellow,red) of the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.timed_out', + 'Timed out status (0:false,1:true) of the elasticsearch cluster', + 'Maps the cluster timed-out status to a numeric value for alarming'); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.number_of_nodes', + 'Number of nodes in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.number_of_data_nodes', + 'Number of data nodes in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.active_primary_shards', + 'Number of active primary shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.active_shards', + 'Number of primary shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.relocating_shards', + 'Number of relocating shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.initializing_shards', + 'Number of initializing shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.unassigned_shards', + 'Number of unassigned shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(0,10), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.status.code', + 'Status code (0:green,1:yellow,2:red) of the elasticsearch cluster', + 'Maps the cluster status colour to a numeric value for alarming'); + +# node stats +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U64, $nodes_indom, # deprecated + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.indices.size_in_bytes', '', ''); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U64, $nodes_indom, # deprecated + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.docs.count', '', ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.docs.num_docs', + 'Raw number of documents indexed by elasticsearch', ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.cache.field_evictions', '', ''); +$pmda->add_metric(pmda_pmid(1,4), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.indices.cache.field_size_in_bytes', '', ''); +$pmda->add_metric(pmda_pmid(1,5), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.cache.filter_count', '', ''); +$pmda->add_metric(pmda_pmid(1,6), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.cache.filter_evictions', '', ''); +$pmda->add_metric(pmda_pmid(1,7), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.indices.cache.filter_size_in_bytes', '', ''); +$pmda->add_metric(pmda_pmid(1,8), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.merges.current', '', ''); +$pmda->add_metric(pmda_pmid(1,9), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.merges.total', '', ''); +$pmda->add_metric(pmda_pmid(1,10), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.merges.total_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,11), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.jvm.uptime_in_millis', + 'Number of milliseconds each elasticsearch node has been running', ''); +$pmda->add_metric(pmda_pmid(1,12), PM_TYPE_STRING, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.uptime', + 'Time (as a string) that each elasticsearch node has been up', ''); +$pmda->add_metric(pmda_pmid(1,13), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.heap_used_in_bytes', + 'Actual amount of memory in use for the Java heap', ''); +$pmda->add_metric(pmda_pmid(1,14), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.heap_committed_in_bytes', + 'Virtual memory size', ''); +$pmda->add_metric(pmda_pmid(1,15), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.non_heap_used_in_bytes', + 'Actual memory in use by Java excluding heap space', ''); +$pmda->add_metric(pmda_pmid(1,16), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.non_heap_committed_in_bytes', + 'Virtual memory size excluding heap', ''); +$pmda->add_metric(pmda_pmid(1,17), PM_TYPE_U32, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.threads.count', + 'Number of Java threads currently in use on each node', ''); +$pmda->add_metric(pmda_pmid(1,18), PM_TYPE_U32, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.threads.peak_count', + 'Maximum observed Java threads in use on each node', ''); +$pmda->add_metric(pmda_pmid(1,19), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.jvm.gc.collection_count', + 'Count of Java garbage collections', ''); +$pmda->add_metric(pmda_pmid(1,20), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.jvm.gc.collection_time_in_millis', + 'Time spent performing garbage collections in Java', ''); +$pmda->add_metric(pmda_pmid(1,21), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.jvm.gc.collectors.Copy.collection_count', + '', ''); +$pmda->add_metric(pmda_pmid(1,22), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.jvm.gc.collectors.Copy.collection_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,23), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.jvm.gc.collectors.ParNew.collection_count', + '', ''); +$pmda->add_metric(pmda_pmid(1,24), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.jvm.gc.collectors.ParNew.collection_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,25), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.jvm.gc.collectors.ConcurrentMarkSweep.collection_count', + '', ''); +$pmda->add_metric(pmda_pmid(1,26), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.jvm.gc.collectors.ConcurrentMarkSweep.collection_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,27), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.docs.deleted', + '', ''); +$pmda->add_metric(pmda_pmid(1,28), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.indexing.index_total', + '', ''); +$pmda->add_metric(pmda_pmid(1,29), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.indexing.index_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,30), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.indexing.delete_total', + '', ''); +$pmda->add_metric(pmda_pmid(1,31), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.indexing.delete_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,32), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.merges.current_docs', + '', ''); +$pmda->add_metric(pmda_pmid(1,33), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.indices.merges.current_size_in_bytes', + '', ''); +$pmda->add_metric(pmda_pmid(1,34), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.merges.total_docs', + '', ''); +$pmda->add_metric(pmda_pmid(1,35), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.indices.merges.total_size_in_bytes', + '', ''); +$pmda->add_metric(pmda_pmid(1,36), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.refresh.total', + '', ''); +$pmda->add_metric(pmda_pmid(1,37), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.refresh.total_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,38), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.flush.total', + '', ''); +$pmda->add_metric(pmda_pmid(1,39), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.flush.total_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,40), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.process.timestamp', '', ''); +$pmda->add_metric(pmda_pmid(1,41), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.process.open_file_descriptors', '', ''); +$pmda->add_metric(pmda_pmid(1,42), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.process.cpu.percent', '', ''); +$pmda->add_metric(pmda_pmid(1,43), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.process.cpu.sys_in_millis', '', ''); +$pmda->add_metric(pmda_pmid(1,44), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.process.cpu.user_in_millis', '', ''); +$pmda->add_metric(pmda_pmid(1,45), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.process.mem.resident_in_bytes', '', ''); +$pmda->add_metric(pmda_pmid(1,46), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.process.mem.total_virtual_in_bytes', + '', ''); +$pmda->add_metric(pmda_pmid(1,47), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.indices.store.size_in_bytes', + 'Size of indices store on each elasticsearch node', ''); +$pmda->add_metric(pmda_pmid(1,48), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.get.total', + '', ''); +$pmda->add_metric(pmda_pmid(1,49), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.get.time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,50), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.get.exists_total', + '', ''); +$pmda->add_metric(pmda_pmid(1,51), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.get.exists_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,52), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.get.missing_total', + '', ''); +$pmda->add_metric(pmda_pmid(1,53), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.get.missing_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,54), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.search.query_total', + '', ''); +$pmda->add_metric(pmda_pmid(1,55), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.search.query_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,56), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.indices.search.fetch_total', + '', ''); +$pmda->add_metric(pmda_pmid(1,57), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.nodes.indices.search.fetch_time_in_millis', + '', ''); +$pmda->add_metric(pmda_pmid(1,58), PM_TYPE_U32, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.transport.server_open', + 'Count of open server connections', ''); +$pmda->add_metric(pmda_pmid(1,59), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.transport.rx_count', + 'Receive transaction count', ''); +$pmda->add_metric(pmda_pmid(1,60), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.transport.rx_size_in_bytes', + 'Receive transaction size', ''); +$pmda->add_metric(pmda_pmid(1,61), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.transport.tx_count', + 'Transmit transaction count', ''); +$pmda->add_metric(pmda_pmid(1,62), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.transport.tx_size_in_bytes', + 'Transmit transaction size', ''); +$pmda->add_metric(pmda_pmid(1,63), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.http.current_open', + 'Number of currently open http connections', ''); +$pmda->add_metric(pmda_pmid(1,64), PM_TYPE_U64, $nodes_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.nodes.http.total_opened', + 'Count of http connections opened since starting', ''); + +# node info stats +$pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $nodes_indom, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.pid', + 'Process identifier for elasticsearch on each node', ''); +$pmda->add_metric(pmda_pmid(2,1), PM_TYPE_STRING, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.version', + 'Java Runtime environment version', ''); +$pmda->add_metric(pmda_pmid(2,2), PM_TYPE_STRING, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.vm_name', + 'Name of the Java Virtual Machine running on each node', ''); +$pmda->add_metric(pmda_pmid(2,3), PM_TYPE_STRING, $nodes_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.jvm.vm_version', + 'Java Virtual Machine version on each node', ''); +$pmda->add_metric(pmda_pmid(2,4), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.heap_init_in_bytes', + 'Initial Java heap memory configuration size', ''); +$pmda->add_metric(pmda_pmid(2,5), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.heap_max_in_bytes', + 'Maximum Java memory size', ''); +$pmda->add_metric(pmda_pmid(2,6), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.non_heap_init_in_bytes', + 'Initial Java memory configuration size excluding heap space', ''); +$pmda->add_metric(pmda_pmid(2,7), PM_TYPE_U64, $nodes_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'elasticsearch.nodes.jvm.mem.non_heap_max_in_bytes', + 'Maximum Java memory size excluding heap space', ''); +$pmda->add_metric(pmda_pmid(2,8), PM_TYPE_U64, $nodes_indom, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'elasticsearch.nodes.process.max_file_descriptors', '', ''); + +$pmda->add_metric(pmda_pmid(3,0), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'elasticsearch.version.number', + 'Version number of elasticsearch', ''); + +$pmda->add_metric(pmda_pmid(4,0), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.search.shards.total', + 'Number of shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(4,1), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.search.shards.successful', + 'Number of successful shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(4,2), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.search.shards.failed', + 'Number of failed shards in the elasticsearch cluster', ''); +$pmda->add_metric(pmda_pmid(4,3), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.all.primaries.search.query_total', + 'Number of search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,4), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.all.primaries.search.query_time_in_millis', + 'Time spent in search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,5), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.all.primaries.search.fetch_total', + 'Number of search fetches to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,6), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.all.primaries.search.fetch_time_in_millis', + 'Time spent in search fetches to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,7), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.all.total.search.query_total', + 'Number of search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,8), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.all.total.search.query_time_in_millis', + 'Time spent in search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,9), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.all.total.search.fetch_total', + 'Number of search fetches to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(4,10), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.all.total.search.fetch_time_in_millis', + 'Time spent in search fetches to all elasticsearch primaries', ''); + +$pmda->add_metric(pmda_pmid(5,0), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.perindex.primaries.search.query_total', + 'Number of search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,1), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.perindex.primaries.search.query_time_in_millis', + 'Time spent in search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,2), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.perindex.primaries.search.fetch_total', + 'Number of search fetches to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,3), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.perindex.primaries.search.fetch_time_in_millis', + 'Time spent in search fetches to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,4), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.perindex.total.search.query_total', + 'Number of search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,5), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.perindex.total.search.query_time_in_millis', + 'Time spent in search queries to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,6), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'elasticsearch.search.perindex.total.search.fetch_total', + 'Number of search fetches to all elasticsearch primaries', ''); +$pmda->add_metric(pmda_pmid(5,7), PM_TYPE_U64, $search_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.search.perindex.total.search.fetch_time_in_millis', + 'Time spent in search fetches to all elasticsearch primaries', ''); + +# cluster state +$pmda->add_metric(pmda_pmid(6,0), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.cluster.master_node', + 'Internal identifier of the master node of the cluster', ''); + +# index state +$pmda->add_metric(pmda_pmid(7,0), PM_TYPE_64, $index_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'elasticsearch.index.settings.gateway_snapshot_interval', + 'Interval between gateway snapshots', ''); +$pmda->add_metric(pmda_pmid(7,1), PM_TYPE_U64, $index_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.index.settings.number_of_replicas', + 'Number of replicas of shards index setting', ''); +$pmda->add_metric(pmda_pmid(7,2), PM_TYPE_U64, $index_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.index.settings.number_of_shards', + 'Number of shards index setting', ''); +$pmda->add_metric(pmda_pmid(7,3), PM_TYPE_U64, $index_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'elasticsearch.index.settings.version_created', + 'The version of elasticsearch the index was created with', ''); + +$pmda->add_indom($nodes_indom, \@nodes_instances, + 'Instance domain exporting each elasticsearch node', ''); +$pmda->add_indom($search_indom, \@search_instances, + 'Instance domain exporting each elasticsearch index', ''); +$pmda->add_indom($index_indom, \@index_instances, + 'Instance domain exporting each elasticsearch index metadata', ''); + +$pmda->set_fetch_callback(\&es_fetch_callback); +$pmda->set_refresh(\&es_refresh); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdaelasticsearch - elasticsearch performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from elasticsearch. + + +=head1 INSTALLATION + +This PMDA requires that elasticsearch is running on the local host and +is accepting queries on TCP port 9200 + + +Install the elasticsearch PMDA by using the B script as root: + + # cd $PCP_PMDAS_DIR/elasticsearch + # ./Install + +To uninstall, do the following as root: + + # cd $PCP_PMDAS_DIR/elasticsearch + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/elasticsearch/es.conf + +optional configuration file for B + +elasticsearch PMDA for PCP + +=item $PCP_PMDAS_DIR/elasticsearch/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/elasticsearch/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/elasticsearch.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/etw/GNUmakefile b/src/pmdas/etw/GNUmakefile new file mode 100644 index 0000000..2951076 --- /dev/null +++ b/src/pmdas/etw/GNUmakefile @@ -0,0 +1,78 @@ +# +# Copyright (c) 2011, Nathan Scott. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs +WINDIR = $(TOPDIR)/src/win32ctl + +IAM = etw +DOMAIN = ETW +CFILES = util.c event.c pmda.c +HFILES = util.h event.h +LSRCFILES = tdhlist.c tdhconsume.c pcp.xml pmns root help +LCFLAGS = -I$(WINDIR)/include -D_WIN32_WINNT=0x0600 -DFORCEINLINE="static inline" +LLDFLAGS = -L$(WINDIR)/lib +LLDLIBS = $(PCP_PMDALIB) -lpcp_tdh + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LIBTARGET = pmda_etw.dll +EXTRATARGETS = tdhlist.exe tdhconsume.exe help.dir help.pag +LDIRT = $(EXTRATARGETS) root_etw domain.h $(IAM).log + +CONF_LINE = "etw 87 dso etw_init $(PCP_PMDAS_DIR)/etw/pmda_etw.dll" + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "mingw" +build-me: root_etw $(LSRCFILES) $(LIBTARGET) $(EXTRATARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 tdhlist.exe $(PMDADIR)/tdhlist.exe + $(INSTALL) -m 644 pmns $(PMDADIR)/root + $(INSTALL) -m 644 domain.h help.dir help.pag help $(PMDADIR) + $(INSTALL) -m 644 root_etw $(PCP_VAR_DIR)/pmns/root_etw +else +build-me: +install: +endif + +help.dir help.pag : help root_etw + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_etw -v 2 -o help < help + +default_pcp: default + +install_pcp: install + +root_etw: ../../pmns/stdpmid $(PMNS) + rm -f root_etw + sed -e 's;;"../../pmns/stdpmid";' root_etw + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(OBJECTS): domain.h util.h + +tdhlist.exe: tdhlist.o util.o + $(CCF) -o tdhlist.exe tdhlist.o util.o $(LLDFLAGS) -lpcp_tdh + +tdhconsume.exe: tdhconsume.o util.o + $(CCF) -o tdhconsume.exe tdhconsume.o util.o $(LLDFLAGS) -lpcp_tdh -lwsock32 diff --git a/src/pmdas/etw/event.c b/src/pmdas/etw/event.c new file mode 100644 index 0000000..f07aa5e --- /dev/null +++ b/src/pmdas/etw/event.c @@ -0,0 +1,251 @@ +/* + * Event queue support for the ETW PMDA + * + * Copyright (c) 2011 Nathan Scott. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "event.h" +#include "util.h" + +static struct { + TRACEHANDLE session; + EVENT_TRACE_PROPERTIES properties; + EVENT_TRACE_LOGFILE tracemode; + BOOL sequence; + ULONG enabled; + + /* session stats */ + ULONG buffer_count; + ULONG buffer_size; + ULONG buffer_bytes; + ULONG buffer_reads; + ULONG events_lost; +} sys; + +ULONG WINAPI +event_buffer_callback(PEVENT_TRACE_LOGFILE mode) +{ + sys.buffer_count++; + sys.buffer_size = mode->BufferSize; + sys.buffer_reads += mode->BuffersRead; + sys.buffer_bytes += mode->Filled; + sys.events_lost += mode->EventsLost; + return TRUE; +} + +/* Difference in seconds between 1/1/1970 and 1/1/1601 */ +#define EPOCH_DELTA_IN_MICROSEC 11644473600000000ULL + +static void +event_decode_timestamp(PEVENT_RECORD event, struct timeval *tv) +{ + FILETIME ft; /* 100-nanosecond intervals since 1/1/1601 */ + ft.dwHighDateTime = event->EventHeader.TimeStamp.HighPart; + ft.dwLowDateTime = event->EventHeader.TimeStamp.LowPart; + + __uint64_t tmp = 0; + tmp |= ft.dwHighDateTime; + tmp <<= 32; + tmp |= ft.dwLowDateTime; + tmp /= 10; /* convert to microseconds */ + tmp -= EPOCH_DELTA_IN_MICROSEC; /* convert Win32 -> Unix epoch */ + + tv->tv_sec = tmp / 1000000UL; + tv->tv_usec = tmp % 1000000UL; +} + +void +event_duplicate_record(PEVENT_RECORD source, PEVENT_RECORD target) +{ + memcpy(source, target, sizeof(EVENT_RECORD)); +} + +void +event_duplicate_buffer(PEVENT_RECORD source, char *buffer, size_t *bytes) +{ + PTRACE_EVENT_INFO pinfo = (PTRACE_EVENT_INFO)buffer; + DWORD size = DEFAULT_MAXMEM, sts; + + sts = TdhGetEventInformation(source, 0, NULL, pinfo, &size); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + *bytes = 0; /* too large for us, too bad, bail out */ + } else { + *bytes = size; + } +} + +/* + * This is the main event callback routine. It uses a fixed + * maximally sized buffer to capture and do minimal decoding + * of the event, before passing it into a queue. Avoid any + * memory allocation and/or copying here where possible, as + * this is the event arrival fast path. + * Note: if there are no clients, the queuing code will drop + * this event (within pmdaEventQueueAppend), so don't waste + * time here - basically just lookup the queue and dispatch. + */ +VOID WINAPI +event_record_callback(PEVENT_RECORD event) +{ + size_t bytes; + struct timeval timestamp; + struct { + EVENT_RECORD record; + char buffer[DEFAULT_MAXMEM]; + } localevent; + etw_event_t *entry; + + LPGUID guid = &event->EventHeader.ProviderId; + int eventid = event->EventHeader.EventDescriptor.Id; + int version = event->EventHeader.EventDescriptor.Version; + + if ((entry = event_table_lookup(guid, eventid, version)) != NULL) { + + event_decode_timestamp(event, ×tamp); + event_duplicate_record(event, &localevent.record); + event_duplicate_buffer(event, &localevent.buffer[0], &bytes); + bytes += sizeof(EVENT_RECORD); + + event_queue_lock(entry); + pmdaEventQueueAppend(entry->queueid, &localevent, bytes, ×tamp); + event_queue_unlock(entry); + + } else { + __pmNotifyErr(LOG_ERR, "failed to enqueue event %d:%d from provider %s", + eventid, version, strguid(guid)); + } +} + +static void +event_sys_setup(void) +{ + ULONG size = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME); + + sys.properties.Wnode.BufferSize = size; + sys.properties.Wnode.Flags = WNODE_FLAG_TRACED_GUID; + sys.properties.Wnode.ClientContext = 1; + sys.properties.Wnode.Guid = SystemTraceControlGuid; + sys.properties.EnableFlags = sys.enabled; + sys.properties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + if (sys.sequence) + sys.properties.LogFileMode |= EVENT_TRACE_USE_GLOBAL_SEQUENCE; + sys.properties.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + sys.tracemode.LoggerName = KERNEL_LOGGER_NAME; + sys.tracemode.BufferCallback = event_buffer_callback; + sys.tracemode.EventRecordCallback = event_record_callback; + sys.tracemode.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME; +} + +/* Kernel tracing thread main() */ +static DWORD WINAPI +event_trace_sys(LPVOID ptr) +{ + ULONG sts, retried = 0; + TRACEHANDLE trace; + +retry_session: + event_sys_setup(); + + sts = StartTrace(&sys.session, KERNEL_LOGGER_NAME, &sys.properties); + if (sts != ERROR_SUCCESS) { + if (retried) { + __pmNotifyErr(LOG_ERR, "Cannot start session (in use)"); + } else if (sts == ERROR_ALREADY_EXISTS) { + __pmNotifyErr(LOG_WARNING, "%s session in use, retry.. (flags=%lx)", + KERNEL_LOGGER_NAME, sys.enabled); + sys.properties.EnableFlags = 0; + ControlTrace(sys.session, KERNEL_LOGGER_NAME, + &sys.properties, EVENT_TRACE_CONTROL_STOP); + retried = 1; + goto retry_session; + } + return sts; + } + + trace = OpenTrace(&sys.tracemode); + if (trace == INVALID_PROCESSTRACE_HANDLE) { + sts = GetLastError(); + __pmNotifyErr(LOG_ERR, "failed to open kernel trace: %s (%lu)", + tdherror(sts), sts); + return sts; + } + + sts = ProcessTrace(&trace, 1, NULL, NULL); /* blocks, awaiting events */ + if (sts == ERROR_CANCELLED) + sts = ERROR_SUCCESS; + if (sts != ERROR_SUCCESS) + __pmNotifyErr(LOG_ERR, "failed to process kernel traces: %s (%lu)", + tdherror(sts), sts); + return sts; +} + +void +event_queue_lock(etw_event_t *entry) +{ + WaitForSingleObject(entry->mutex, INFINITE); +} + +void +event_queue_unlock(etw_event_t *entry) +{ + ReleaseMutex(entry->mutex); +} + +int +event_init(void) +{ + HANDLE thread; + + __pmNotifyErr(LOG_INFO, "%s: Starting up tracing ...", __FUNCTION__); + thread = CreateThread(NULL, 0, event_trace_sys, &sys, 0, NULL); + if (thread == NULL) + return -ECHILD; + CloseHandle(thread); /* no longer need the handle */ + return 0; +} + +void __attribute__((constructor)) +event_startup(void) +{ + sys.session = INVALID_PROCESSTRACE_HANDLE; +} + +void __attribute__((destructor)) +event_shutdown(void) +{ + __pmNotifyErr(LOG_INFO, "%s: Shutting down tracing ...", __FUNCTION__); + if (sys.session != INVALID_PROCESSTRACE_HANDLE) { + sys.properties.EnableFlags = 0; + ControlTrace(sys.session, 0, &sys.properties, EVENT_TRACE_CONTROL_STOP); + sys.session = INVALID_PROCESSTRACE_HANDLE; + } +} + +int +event_decoder(int eventarray, void *buffer, size_t size, + struct timeval *timestamp, void *data) +{ + int sts; /* , handle = *(int *)data; */ + pmAtomValue atom; + pmID pmid = 0; /* TODO */ + + sts = pmdaEventAddRecord(eventarray, timestamp, PM_EVENT_FLAG_POINT); + if (sts < 0) + return sts; + atom.cp = buffer; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_STRING, &atom); + if (sts < 0) + return sts; + return 1; /* simple decoder, added just one event array */ +} diff --git a/src/pmdas/etw/event.h b/src/pmdas/etw/event.h new file mode 100644 index 0000000..1f39ef0 --- /dev/null +++ b/src/pmdas/etw/event.h @@ -0,0 +1,94 @@ +/* + * Event queue support for the ETW PMDA + * + * Copyright (c) 2011 Nathan Scott. All rights reversed. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _EVENT_H +#define _EVENT_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include +#include + +enum { + CLUSTER_KERNEL_PROCESS = 0, + CLUSTER_KERNEL_THREAD = 1, + CLUSTER_KERNEL_IMAGE_LOAD = 2, + CLUSTER_KERNEL_DISK_IO = 3, + CLUSTER_KERNEL_DISK_FILE_IO = 4, + CLUSTER_KERNEL_MEMORY_PAGE_FAULTS = 5, + CLUSTER_KERNEL_MEMORY_HARD_FAULTS = 6, + CLUSTER_KERNEL_NETWORK_TCPIP = 7, + CLUSTER_KERNEL_REGISTRY = 8, + CLUSTER_KERNEL_DBGPRINT = 9, + CLUSTER_KERNEL_PROCESS_COUNTERS = 10, + CLUSTER_KERNEL_CSWITCH = 11, + CLUSTER_KERNEL_DPC = 12, + CLUSTER_KERNEL_INTERRUPT = 13, + CLUSTER_KERNEL_SYSTEMCALL = 14, + CLUSTER_KERNEL_DISK_IO_INIT = 15, + CLUSTER_KERNEL_ALPC = 16, + CLUSTER_KERNEL_SPLIT_IO = 17, + CLUSTER_KERNEL_DRIVER = 18, + CLUSTER_KERNEL_PROFILE = 19, + CLUSTER_KERNEL_FILE_IO = 20, + CLUSTER_KERNEL_FILE_IO_INIT = 21, + CLUSTER_KERNEL_DISPATCHER = 22, + CLUSTER_KERNEL_VIRTUAL_ALLOC = 23, + CLUSTER_KERNEL_EXTENSION = 24, + CLUSTER_KERNEL_FORWARD_WMI = 25, + CLUSTER_KERNEL_ENABLE_RESERVE = 26, + + CLUSTER_SQLSERVER_RPC_STARTING = 30, + CLUSTER_SQLSERVER_BATCH_STARTING = 31, + + CLUSTER_CONFIGURATION = 250 +}; + +enum { + EVENT_PROCESS_START, + EVENT_PROCESS_EXIT, + EVENT_THREAD_START, + EVENT_THREAD_STOP, + EVENT_IMAGE_LOAD, + EVENT_IMAGE_UNLOAD, +}; + +#define DEFAULT_MAXMEM (128 * 1024) /* 128K per event queue */ + +typedef struct { + int queueid; + int eventid; + int version; + ULONG flags; + HANDLE mutex; + char pmnsname[64]; + const GUID *provider; + LPTSTR pname; + ULONG plen; +} etw_event_t; + +extern etw_event_t eventtab[]; +extern int event_init(void); +extern void event_shutdown(void); +extern int event_decoder(int arrayid, void *buffer, size_t size, + struct timeval *timestamp, void *data); + +extern etw_event_t *event_table_lookup(LPGUID guid, int eventid, int version); + +void event_queue_lock(etw_event_t *entry); +void event_queue_unlock(etw_event_t *entry); + +#endif /* _EVENT_H */ diff --git a/src/pmdas/etw/help b/src/pmdas/etw/help new file mode 100644 index 0000000..a48a3fc --- /dev/null +++ b/src/pmdas/etw/help @@ -0,0 +1,68 @@ +/* + * Event Tracing for Windows PMDA + * + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +@ etw.kernel.process.start.count +@ etw.kernel.process.start.records +@ etw.kernel.process.start.numclients +@ etw.kernel.process.start.queuemem +@ etw.kernel.process.start.params.activityid +@ etw.kernel.process.start.params.pid +@ etw.kernel.process.start.params.parentid +@ etw.kernel.process.start.params.starttime +@ etw.kernel.process.start.params.session +@ etw.kernel.process.start.params.image_name +@ etw.kernel.process.exit.count +@ etw.kernel.process.exit.numclients +@ etw.kernel.process.exit.queuemem +@ etw.kernel.process.exit.params.activityid +@ etw.kernel.process.exit.params.pid +@ etw.kernel.process.exit.params.parentid +@ etw.kernel.process.exit.params.starttime +@ etw.kernel.process.exit.params.exittime +@ etw.kernel.process.exit.params.exitcode +@ etw.kernel.process.exit.params.handle_count +@ etw.kernel.process.exit.params.commit_charge +@ etw.kernel.process.exit.params.commit_peak +@ etw.kernel.process.exit.params.image_name +@ etw.kernel.process.thread.start.count +@ etw.kernel.process.thread.start.records +@ etw.kernel.process.thread.start.numclients +@ etw.kernel.process.thread.start.queuemem +@ etw.kernel.process.thread.start.params.tid +@ etw.kernel.process.thread.start.params.pid +@ etw.kernel.process.thread.stop.count +@ etw.kernel.process.thread.stop.records +@ etw.kernel.process.thread.stop.numclients +@ etw.kernel.process.thread.stop.queuemem +@ etw.kernel.process.thread.stop.params.activityid +@ etw.kernel.process.thread.stop.params.tid +@ etw.kernel.process.thread.stop.params.pid +@ etw.kernel.process.image_load.count +@ etw.kernel.process.image_load.records +@ etw.kernel.process.image_load.numclients +@ etw.kernel.process.image_load.queuemem +@ etw.kernel.process.image_load.params.activityid +@ etw.kernel.process.image_load.params.pid +@ etw.kernel.process.image_load.params.name +@ etw.kernel.process.image_load.params.size +@ etw.kernel.process.image_unload.count +@ etw.kernel.process.image_unload.records +@ etw.kernel.process.image_unload.numclients +@ etw.kernel.process.image_unload.queuemem +@ etw.kernel.process.image_unload.params.activityid +@ etw.kernel.process.image_unload.params.pid +@ etw.kernel.process.image_unload.params.name +@ etw.kernel.process.image_unload.params.size diff --git a/src/pmdas/etw/pcp.xml b/src/pmdas/etw/pcp.xml new file mode 100644 index 0000000..d87f6a0 Binary files /dev/null and b/src/pmdas/etw/pcp.xml differ diff --git a/src/pmdas/etw/pmda.c b/src/pmdas/etw/pmda.c new file mode 100644 index 0000000..5a802bf --- /dev/null +++ b/src/pmdas/etw/pmda.c @@ -0,0 +1,513 @@ +/* + * Event Tracing for Windows PMDA + * + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "event.h" +#include "domain.h" +#include "util.h" + +etw_event_t eventtab[] = { + { EVENT_PROCESS_START, 0, 1, EVENT_TRACE_FLAG_PROCESS, NULL, + "process.start", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_PROCESS_EXIT, 2, 1, EVENT_TRACE_FLAG_PROCESS, NULL, + "process.exit", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_THREAD_START, 3, 1, EVENT_TRACE_FLAG_THREAD, NULL, + "thread.start", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_THREAD_STOP, 4, 1, EVENT_TRACE_FLAG_THREAD, NULL, + "thread.stop", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_IMAGE_LOAD, 5, 1, EVENT_TRACE_FLAG_IMAGE_LOAD, NULL, + "image.load", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, + { EVENT_IMAGE_UNLOAD, 6, 1, EVENT_TRACE_FLAG_IMAGE_LOAD, NULL, + "image.unload", &SystemTraceControlGuid, + KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME) }, +}; + +#if 0 +static etw_event_t etwevents[] = { + { CLUSTER_KERNEL_PROCESS, 0, EVENT_TRACE_FLAG_PROCESS, + { CLUSTER_KERNEL_THREAD, 0, + EVENT_TRACE_FLAG_THREAD, "kernel.thread" }, + { CLUSTER_KERNEL_IMAGE_LOAD, 0, + EVENT_TRACE_FLAG_IMAGE_LOAD, "kernel.image_load" }, + { CLUSTER_KERNEL_DISK_IO, 0, + EVENT_TRACE_FLAG_DISK_IO, "kernel.disk_io" }, + { CLUSTER_KERNEL_DISK_FILE_IO, 0, + EVENT_TRACE_FLAG_DISK_FILE_IO, "kernel.disk_file_io" }, + { CLUSTER_KERNEL_MEMORY_PAGE_FAULTS, 0, + EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS, "kernel.memory_page_faults" }, + { CLUSTER_KERNEL_MEMORY_HARD_FAULTS, 0, + EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS, "kernel.memory_hard_faults" }, + { CLUSTER_KERNEL_NETWORK_TCPIP, 0, + EVENT_TRACE_FLAG_NETWORK_TCPIP, "kernel.network_tcpip" }, + { CLUSTER_KERNEL_REGISTRY, 0, + EVENT_TRACE_FLAG_REGISTRY, "kernel.registry" }, + { CLUSTER_KERNEL_DBGPRINT, 0, + EVENT_TRACE_FLAG_DBGPRINT, "kernel.dbgprint" }, + { CLUSTER_KERNEL_PROCESS_COUNTERS, 0, + EVENT_TRACE_FLAG_PROCESS_COUNTERS, "kernel.process_counters" }, + { CLUSTER_KERNEL_CSWITCH, 0, + EVENT_TRACE_FLAG_CSWITCH, "kernel.cswitch" }, + { CLUSTER_KERNEL_DPC, 0, + EVENT_TRACE_FLAG_DPC, "kernel.dpc" }, + { CLUSTER_KERNEL_INTERRUPT, 0, + EVENT_TRACE_FLAG_INTERRUPT, "kernel.interrupt" }, + { CLUSTER_KERNEL_SYSTEMCALL, 0, + EVENT_TRACE_FLAG_SYSTEMCALL, "kernel.syscall" }, + { CLUSTER_KERNEL_DISK_IO_INIT, 0, + EVENT_TRACE_FLAG_DISK_IO_INIT, "kernel.disk_io_init" }, + { CLUSTER_KERNEL_ALPC, 0, + EVENT_TRACE_FLAG_ALPC, "kernel.alpc" }, + { CLUSTER_KERNEL_SPLIT_IO, 0, + EVENT_TRACE_FLAG_SPLIT_IO, "kernel.split_io" }, + { CLUSTER_KERNEL_DRIVER, 0, + EVENT_TRACE_FLAG_DRIVER, "kernel.driver" }, + { CLUSTER_KERNEL_PROFILE, 0, + EVENT_TRACE_FLAG_PROFILE, "kernel.profile" }, + { CLUSTER_KERNEL_FILE_IO, 0, + EVENT_TRACE_FLAG_FILE_IO, "kernel.file_io" }, + { CLUSTER_KERNEL_FILE_IO_INIT, 0, + EVENT_TRACE_FLAG_FILE_IO_INIT, "kernel.file_io_init" }, + { CLUSTER_KERNEL_DISPATCHER, 0, + EVENT_TRACE_FLAG_DISPATCHER, "kernel.dispatcher" }, + { CLUSTER_KERNEL_VIRTUAL_ALLOC, 0, + EVENT_TRACE_FLAG_VIRTUAL_ALLOC, "kernel.virtual_alloc" }, + { CLUSTER_KERNEL_EXTENSION, 0, + EVENT_TRACE_FLAG_EXTENSION, "kernel.extension" }, + { CLUSTER_KERNEL_FORWARD_WMI, 0, + EVENT_TRACE_FLAG_FORWARD_WMI, "kernel.forward_wmi" }, + { CLUSTER_KERNEL_ENABLE_RESERVE, 0, + EVENT_TRACE_FLAG_ENABLE_RESERVE, "kernel.enable_reserve" }, + + { CLUSTER_SQLSERVER_RPC_STARTING, 0, 0, "sqlserver.rpc.starting" }, + { CLUSTER_SQLSERVER_BATCH_STARTING, 0, 0, "sqlserver.sql.batch_starting" }, +}; +static int numqueues = sizeof(etwevents)/sizeof(etw_event_t); +#endif + + +static pmdaMetric metrictab[] = { +/* etw.kernel.process.start.count */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.start.records */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,1), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.numclients */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.start.queuemem */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,3), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.start.params.activityid */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,10), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.pid */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,11), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.parentid */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,12), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.starttime */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,13), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* etw.kernel.process.start.params.session */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,14), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.start.params.image_name */ + { &eventtab[EVENT_PROCESS_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,15), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.exit.count */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,20), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.exit.records */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,21), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.numclients */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,22), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.exit.queuemem */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,23), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.exit.params.activityid */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,30), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.pid */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,31), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.parentid */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,32), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.starttime */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,33), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* etw.kernel.process.exit.params.exittime */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,34), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* etw.kernel.process.exit.params.exitcode */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,35), PM_TYPE_32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.handle_count */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,36), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.commit_charge */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,37), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.commit_peak */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,38), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.exit.params.image_name */ + { &eventtab[EVENT_PROCESS_EXIT], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,39), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.thread.start.count */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,50), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.start.records */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,51), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.start.numclients */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,52), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.start.queuemem */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,53), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.thread.start.params.activityid */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,60), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.start.params.tid */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,61), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.start.params.pid */ + { &eventtab[EVENT_THREAD_START], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,62), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.thread.stop.count */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,70), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.stop.records */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,71), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.stop.numclients */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,72), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.thread.stop.queuemem */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,73), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.thread.stop.params.activityid */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,80), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.stop.params.tid */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,81), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.thread.stop.params.pid */ + { &eventtab[EVENT_THREAD_STOP], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,82), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.image_load.count */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,90), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_load.records */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,91), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.numclients */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,92), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_load.queuemem */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,93), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.image_load.params.activityid */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,100), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.params.pid */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,101), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.params.name */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,102), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_load.params.size */ + { &eventtab[EVENT_IMAGE_LOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,103), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* etw.kernel.process.image_unload.count */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,110), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_unload.records */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,111), PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.numclients */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,112), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* etw.kernel.process.image_unload.queuemem */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,113), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* etw.kernel.process.image_unload.params.activityid */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,120), PM_TYPE_AGGREGATE, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.params.pid */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,121), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.params.name */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,122), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* etw.kernel.process.image_unload.params.size */ + { &eventtab[EVENT_IMAGE_UNLOAD], + { PMDA_PMID(CLUSTER_KERNEL_PROCESS,123), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +static int +etw_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + etw_event_t *etw; + int sts = PMDA_FETCH_STATIC; + + __pmNotifyErr(LOG_WARNING, "called %s, mdesc=%p", __FUNCTION__, mdesc); + + switch (idp->cluster) { + case CLUSTER_KERNEL_PROCESS: + if ((etw = ((mdesc != NULL) ? mdesc->m_user : NULL)) == NULL) + return PM_ERR_PMID; + + switch (idp->item) { + case 0: /* etw.kernel.process.start.count */ + case 20: /* etw.kernel.process.exit.count */ + case 50: /* etw.kernel.thread.start.count */ + case 70: /* etw.kernel.thread.stop.count */ + case 90: /* etw.kernel.process.image_load.count */ + case 110: /* etw.kernel.process.image_unload.count */ + sts = pmdaEventQueueCounter(etw->queueid, atom); + break; + case 1: /* etw.kernel.process.start.records */ + case 21: /* etw.kernel.process.exit.records */ + case 51: /* etw.kernel.thread.start.records */ + case 71: /* etw.kernel.thread.stop.records */ + case 91: /* etw.kernel.process.image_load.records */ + case 111: /* etw.kernel.process.image_unload.records */ + event_queue_lock(etw); + sts = pmdaEventQueueRecords(etw->queueid, atom, + pmdaGetContext(), + event_decoder, &etw); + event_queue_unlock(etw); + break; + case 2: /* etw.kernel.process.start.numclients */ + case 22: /* etw.kernel.process.exit.numclients */ + case 52: /* etw.kernel.thread.start.numclients */ + case 72: /* etw.kernel.thread.stop.numclients */ + case 92: /* etw.kernel.process.image_load.numclients */ + case 112: /* etw.kernel.process.image_unload.numclients */ + sts = pmdaEventQueueClients(etw->queueid, atom); + break; + case 3: /* etw.kernel.process.start.queuemem */ + case 23: /* etw.kernel.process.exit.queuemem */ + case 53: /* etw.kernel.thread.start.queuemem */ + case 73: /* etw.kernel.thread.stop.queuemem */ + case 93: /* etw.kernel.process.image_load.queuemem */ + case 113: /* etw.kernel.process.image_unload.queuemem */ + sts = pmdaEventQueueMemory(etw->queueid, atom); + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_CONFIGURATION: + switch (idp->item) { + case 0: /* etw.numclients */ + sts = pmdaEventClients(atom); + break; + default: + return PM_ERR_PMID; + } + break; + + default: + break; + } + + return sts; +} + +static int +etw_profile(__pmProfile *prof, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return 0; +} + +static int +etw_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + __pmNotifyErr(LOG_WARNING, "called %s", __FUNCTION__); + pmdaEventNewClient(pmda->e_context); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +etw_store(pmResult *result, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return PM_ERR_PERMISSION; +} + +static void +etw_end_contextCallBack(int context) +{ + __pmNotifyErr(LOG_WARNING, "called %s", __FUNCTION__); + pmdaEventEndClient(context); +} + +static int +etw_text(int ident, int type, char **buffer, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaText(ident, type, buffer, pmda); +} + +static int +event_equal(etw_event_t *event, LPGUID provider, int eventid, int version) +{ + if (memcmp(event->provider, provider, sizeof(GUID)) != 0) + return 0; + return (event->eventid == eventid && event->version == version); +} + +etw_event_t * +event_table_lookup(LPGUID guid, int eventid, int version) +{ + static int last; /* punt on previous type of event reoccurring */ + int i = last; + + if (event_equal(&eventtab[i], guid, eventid, version)) + return &eventtab[i]; + + for (i = 0; i < sizeof(eventtab)/sizeof(eventtab[0]); i++) { + etw_event_t *e = &eventtab[i]; + if (event_equal(e, guid, eventid, version)) { + last = i; + return e; + } + } + return NULL; +} + +int +event_table_init(void) +{ + int i, id; + + for (i = 0; i < sizeof(eventtab)/sizeof(eventtab[0]); i++) { + HANDLE mutex = CreateMutex(NULL, FALSE, NULL); + etw_event_t *e = &eventtab[i]; + + if (!mutex) { + __pmNotifyErr(LOG_WARNING, "failed to create mutex for event %s", + e->pmnsname); + } else if ((id = pmdaEventNewQueue(e->pmnsname, DEFAULT_MAXMEM)) < 0) { + __pmNotifyErr(LOG_WARNING, "failed to create queue for event %s", + e->pmnsname); + } else { + e->queueid = id; + e->mutex = mutex; + } + } + return 0; +} + +void +etw_init(pmdaInterface *dp, const char *configfile) +{ + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + + snprintf(helppath, sizeof(helppath), "%s%c" "etw" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_5, "etw DSO", helppath); + if (dp->status != 0) + return; + if (event_table_init() < 0) + return; + if (event_init() < 0) + return; + + dp->version.four.fetch = etw_fetch; + dp->version.four.store = etw_store; + dp->version.four.profile = etw_profile; + dp->version.four.text = etw_text; + + pmdaSetFetchCallBack(dp, etw_fetchCallBack); + pmdaSetEndContextCallBack(dp, etw_end_contextCallBack); + + pmdaInit(dp, NULL, 0, metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} diff --git a/src/pmdas/etw/pmns b/src/pmdas/etw/pmns new file mode 100644 index 0000000..7fc1b06 --- /dev/null +++ b/src/pmdas/etw/pmns @@ -0,0 +1,132 @@ +/* + * Event Tracing for Windows performance metrics namespace + * + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +etw { + kernel +} + +etw.kernel { + process +} + +etw.kernel.process { + start + exit + thread + image_load + image_unload +} + +etw.kernel.process.start { + count ETW:0:0 + records ETW:0:1 + numclients ETW:0:2 + queuemem ETW:0:3 + params +} + +etw.kernel.process.start.params { + activityid ETW:0:10 + pid ETW:0:11 + parentid ETW:0:12 + starttime ETW:0:13 + session ETW:0:14 + image_name ETW:0:15 +} + +etw.kernel.process.exit { + count ETW:0:20 + records ETW:0:21 + numclients ETW:0:22 + queuemem ETW:0:23 + params +} + +etw.kernel.process.exit.params { + activityid ETW:0:30 + pid ETW:0:31 + parentid ETW:0:32 + starttime ETW:0:33 + exittime ETW:0:34 + exitcode ETW:0:35 + handle_count ETW:0:36 + commit_charge ETW:0:37 + commit_peak ETW:0:38 + image_name ETW:0:39 +} + +etw.kernel.process.thread { + start + stop +} + +etw.kernel.process.thread.start { + count ETW:0:50 + records ETW:0:51 + numclients ETW:0:52 + queuemem ETW:0:53 + params +} + +etw.kernel.process.thread.start.params { + activityid ETW:0:60 + tid ETW:0:61 + pid ETW:0:62 +} + +etw.kernel.process.thread.stop { + count ETW:0:70 + records ETW:0:71 + numclients ETW:0:72 + queuemem ETW:0:73 + params +} + +etw.kernel.process.thread.stop.params { + activityid ETW:0:80 + tid ETW:0:81 + pid ETW:0:82 +} + +etw.kernel.process.image_load { + count ETW:0:90 + records ETW:0:91 + numclients ETW:0:92 + queuemem ETW:0:93 + params +} + +etw.kernel.process.image_load.params { + activityid ETW:0:100 + pid ETW:0:101 + name ETW:0:102 + size ETW:0:103 +} + +etw.kernel.process.image_unload { + count ETW:0:110 + records ETW:0:111 + numclients ETW:0:112 + queuemem ETW:0:113 + params +} + +etw.kernel.process.image_unload.params { + activityid ETW:0:120 + pid ETW:0:121 + name ETW:0:122 + size ETW:0:123 +} diff --git a/src/pmdas/etw/root b/src/pmdas/etw/root new file mode 100644 index 0000000..9cfae6d --- /dev/null +++ b/src/pmdas/etw/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { etw } + +#include "pmns" + diff --git a/src/pmdas/etw/tdhconsume.c b/src/pmdas/etw/tdhconsume.c new file mode 100644 index 0000000..6ee3224 --- /dev/null +++ b/src/pmdas/etw/tdhconsume.c @@ -0,0 +1,923 @@ +/* + * Event Trace Consumer for Windows events on the current platform. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include "util.h" + +#define PROPERTY_BUFFER 1024 +#define MAX_SESSIONS 64 +#define PCP_SESSION "PCP Collector Set" + +static int verbose; + +/* Get the event metadata */ +static DWORD +GetEventInformation(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO *pInfoPointer) +{ + PTRACE_EVENT_INFO pInfo = NULL; + DWORD sts = ERROR_SUCCESS; + DWORD size = 0; + + sts = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &size); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + pInfo = (TRACE_EVENT_INFO *)malloc(size); + if (pInfo == NULL) { + fprintf(stderr, + "Failed to allocate memory for event info (size=%lu).\n", size); + sts = ERROR_OUTOFMEMORY; + } else { + /* Retrieve event metadata */ + sts = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &size); + } + } else if (sts != ERROR_SUCCESS && verbose) { + fprintf(stderr, "TdhGetEventInformation failed: %s (%lu)\n", + tdherror(sts), sts); + } + *pInfoPointer = pInfo; + return sts; +} + +static void +PrintMapString(PEVENT_MAP_INFO pMapInfo, PBYTE pData) +{ + BOOL MatchFound = FALSE; + DWORD i; + + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP) == + EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP || + ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == + EVENTMAP_INFO_FLAG_WBEM_VALUEMAP && + (pMapInfo->Flag & (~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP)) != + EVENTMAP_INFO_FLAG_WBEM_FLAG)) { + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == + EVENTMAP_INFO_FLAG_WBEM_NO_MAP) { + printf("%s\n", ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[*(PULONG)pData].OutputOffset)); + } else { + for (i = 0; i < pMapInfo->EntryCount; i++) { + if (pMapInfo->MapEntryArray[i].Value == *(PULONG)pData) { + printf("%s\n", ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)); + MatchFound = TRUE; + break; + } + } + + if (MatchFound == FALSE) + printf("%lu\n", *(PULONG)pData); + } + } + else if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_BITMAP) == + EVENTMAP_INFO_FLAG_MANIFEST_BITMAP || + (pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_BITMAP) == + EVENTMAP_INFO_FLAG_WBEM_BITMAP || + ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == + EVENTMAP_INFO_FLAG_WBEM_VALUEMAP && + (pMapInfo->Flag & (~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP)) == + EVENTMAP_INFO_FLAG_WBEM_FLAG)) { + if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == + EVENTMAP_INFO_FLAG_WBEM_NO_MAP) { + DWORD BitPosition = 0; + + for (i = 0; i < pMapInfo->EntryCount; i++) { + BitPosition = (1 << i); + if ((*(PULONG)pData & BitPosition) == BitPosition) { + printf("%s%s", (MatchFound) ? " | " : "", + ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)); + MatchFound = TRUE; + } + } + } else { + for (i = 0; i < pMapInfo->EntryCount; i++) { + if ((pMapInfo->MapEntryArray[i].Value & *(PULONG)pData) == + pMapInfo->MapEntryArray[i].Value) { + printf("%s%s", (MatchFound) ? " | " : "", + ((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)); + MatchFound = TRUE; + } + } + } + + if (MatchFound) { + printf("\n"); + } else { + printf("%lu\n", *(PULONG)pData); + } + } +} + +static DWORD +FormatAndPrintData(PEVENT_RECORD pEvent, USHORT InType, USHORT OutType, + PBYTE pData, DWORD DataSize, PEVENT_MAP_INFO pMapInfo) +{ + DWORD i, sts = ERROR_SUCCESS; + size_t StringLength = 0; + + switch (InType) { + case TDH_INTYPE_UNICODESTRING: + case TDH_INTYPE_COUNTEDSTRING: + case TDH_INTYPE_REVERSEDCOUNTEDSTRING: + case TDH_INTYPE_NONNULLTERMINATEDSTRING: + if (TDH_INTYPE_COUNTEDSTRING == InType) + StringLength = *(PUSHORT)pData; + else if (TDH_INTYPE_REVERSEDCOUNTEDSTRING == InType) + StringLength = MAKEWORD( + HIBYTE((PUSHORT)pData), LOBYTE((PUSHORT)pData)); + else if (TDH_INTYPE_NONNULLTERMINATEDSTRING == InType) + StringLength = DataSize; + else + StringLength = wcslen((LPWSTR)pData); + printf("%.*s\n", StringLength, pData); + break; + + case TDH_INTYPE_ANSISTRING: + case TDH_INTYPE_COUNTEDANSISTRING: + case TDH_INTYPE_REVERSEDCOUNTEDANSISTRING: + case TDH_INTYPE_NONNULLTERMINATEDANSISTRING: + if (TDH_INTYPE_COUNTEDANSISTRING == InType) + StringLength = *(PUSHORT)pData; + else if (TDH_INTYPE_REVERSEDCOUNTEDANSISTRING == InType) + StringLength = MAKEWORD( + HIBYTE((PUSHORT)pData), LOBYTE((PUSHORT)pData)); + else if (TDH_INTYPE_NONNULLTERMINATEDANSISTRING == InType) + StringLength = DataSize; + else + StringLength = strlen((LPSTR)pData); + printf("%.*s\n", StringLength, pData); + break; + + case TDH_INTYPE_INT8: + printf("%hd\n", *(PCHAR)pData); + break; + + case TDH_INTYPE_UINT8: + if (TDH_OUTTYPE_HEXINT8 == OutType) + printf("0x%x\n", *(PBYTE)pData); + else + printf("%hu\n", *(PBYTE)pData); + break; + + case TDH_INTYPE_INT16: + printf("%hd\n", *(PSHORT)pData); + break; + + case TDH_INTYPE_UINT16: + if (TDH_OUTTYPE_HEXINT16 == OutType) + printf("0x%x\n", *(PUSHORT)pData); + else if (TDH_OUTTYPE_PORT == OutType) + printf("%hu\n", ntohs(*(PUSHORT)pData)); + else + printf("%hu\n", *(PUSHORT)pData); + break; + + case TDH_INTYPE_INT32: + if (TDH_OUTTYPE_HRESULT == OutType) + printf("0x%lx\n", *(PLONG)pData); + else + printf("%ld\n", *(PLONG)pData); + break; + + case TDH_INTYPE_UINT32: + if (TDH_OUTTYPE_HRESULT == OutType || + TDH_OUTTYPE_WIN32ERROR == OutType || + TDH_OUTTYPE_NTSTATUS == OutType || + TDH_OUTTYPE_HEXINT32 == OutType) + printf("0x%lx\n", *(PULONG)pData); + else if (TDH_OUTTYPE_IPV4 == OutType) + printf("%ld.%ld.%ld.%ld\n", (*(PLONG)pData >> 0) & 0xff, + (*(PLONG)pData >> 8) & 0xff, + (*(PLONG)pData >> 16) & 0xff, + (*(PLONG)pData >> 24) & 0xff); + else if (pMapInfo) + PrintMapString(pMapInfo, pData); + else + printf("%lu\n", *(PULONG)pData); + break; + + case TDH_INTYPE_INT64: + printf("%I64d\n", *(PLONGLONG)pData); + break; + + case TDH_INTYPE_UINT64: + if (TDH_OUTTYPE_HEXINT64 == OutType) + printf("0x%I64x\n", *(PULONGLONG)pData); + else + printf("%I64u\n", *(PULONGLONG)pData); + break; + + case TDH_INTYPE_FLOAT: + printf("%f\n", *(PFLOAT)pData); + break; + + case TDH_INTYPE_DOUBLE: + printf("%f\n", *(double*)pData); + break; + + case TDH_INTYPE_BOOLEAN: + printf("%s\n", ((PBOOL)pData == 0) ? "false" : "true"); + break; + + case TDH_INTYPE_BINARY: + if (TDH_OUTTYPE_IPV6 == OutType) + break; + else { + for (i = 0; i < DataSize; i++) + printf("%.2x", pData[i]); + printf("\n"); + } + break; + + case TDH_INTYPE_GUID: + printf("%s\n", strguid((GUID *)pData)); + break; + + case TDH_INTYPE_POINTER: + case TDH_INTYPE_SIZET: + if (EVENT_HEADER_FLAG_32_BIT_HEADER == + (pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER)) + printf("0x%I32x\n", *(PULONG)pData); + else + printf("0x%I64x\n", *(PULONGLONG)pData); + + case TDH_INTYPE_FILETIME: + break; + + case TDH_INTYPE_SYSTEMTIME: + break; + + case TDH_INTYPE_SID: + break; + + case TDH_INTYPE_HEXINT32: + printf("0x%I32x\n", *(PULONG)pData); + break; + + case TDH_INTYPE_HEXINT64: + printf("0x%I64x\n", *(PULONGLONG)pData); + break; + + case TDH_INTYPE_UNICODECHAR: + printf("%c\n", *(PWCHAR)pData); + break; + + case TDH_INTYPE_ANSICHAR: + printf("%C\n", *(PCHAR)pData); + break; + + case TDH_INTYPE_WBEMSID: + break; + + default: + sts = ERROR_NOT_FOUND; + } + + return sts; +} + +/* + * Get the size of the array. + * For MOF-based events, the size is specified in the declaration or using + * the MAX qualifier. For manifest-based events, the property can specify + * the size of the array using the count attribute. + * The count attribue can specify the size directly or specify the name + * of another property in the event data that contains the size. + */ +static void +GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, + USHORT i, PUSHORT ArraySize) +{ + PROPERTY_DATA_DESCRIPTOR DataDescriptor; + DWORD size = 0; + + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == + PropertyParamCount) { + DWORD cnt = 0; /* expecting count to be defined as uint16 or uint32 */ + DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex; + + ZeroMemory(&DataDescriptor, sizeof(PROPERTY_DATA_DESCRIPTOR)); + DataDescriptor.PropertyName = + (ULONGLONG)((PBYTE)(pInfo) + + pInfo->EventPropertyInfoArray[j].NameOffset); + DataDescriptor.ArrayIndex = ULONG_MAX; + TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &size); + TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, size, (PBYTE)&cnt); + *ArraySize = (USHORT)cnt; + } else { + *ArraySize = pInfo->EventPropertyInfoArray[i].count; + } +} + +/* + * Mapped string values defined in a manifest will contain a trailing space + * in the EVENT_MAP_ENTRY structure. Replace the trailing space with a null- + * terminating character, so that bit mapped strings are correctly formatted. + */ +static void +RemoveTrailingSpace(PEVENT_MAP_INFO pMapInfo) +{ + SIZE_T ByteLength = 0; + DWORD i; + + for (i = 0; i < pMapInfo->EntryCount; i++) { + ByteLength = (wcslen((LPWSTR)((PBYTE)pMapInfo + + pMapInfo->MapEntryArray[i].OutputOffset)) - 1) * 2; + *((LPWSTR)((PBYTE)pMapInfo + + (pMapInfo->MapEntryArray[i].OutputOffset + + ByteLength))) = L'\0'; + } +} + +/* + * Both MOF-based events and manifest-based events can specify name/value maps. + * The map values can be integer values or bit values. + * If the property specifies a value map, get the map. + */ +static DWORD +GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, DWORD DecodingSource, + PEVENT_MAP_INFO pMapInfo) +{ + DWORD sts = ERROR_SUCCESS; + DWORD size = 0; + + /* Retrieve required buffer size for map info */ + sts = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &size); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + pMapInfo = (PEVENT_MAP_INFO)malloc(size); + if (pMapInfo == NULL) { + fprintf(stderr, "Failed to allocate map info memory (size=%lu).\n", + size); + return ERROR_OUTOFMEMORY; + } + + /* Retrieve the map info */ + sts = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &size); + } + + if (sts == ERROR_SUCCESS) { + if (DecodingSourceXMLFile == DecodingSource) + RemoveTrailingSpace(pMapInfo); + } else if (sts == ERROR_NOT_FOUND) { + sts = ERROR_SUCCESS; + } else { + fprintf(stderr, "TdhGetEventMapInformation failed: %s (%ld)\n", + tdherror(sts), sts); + } + return sts; +} + +static DWORD +PrintProperties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, + USHORT i, LPWSTR pStructureName, USHORT StructIndex) +{ + DWORD sts = ERROR_SUCCESS; + DWORD LastMember = 0; + USHORT ArraySize = 0; + PEVENT_MAP_INFO pMapInfo = NULL; + PROPERTY_DATA_DESCRIPTOR DataDescriptors[2]; + ULONG DescriptorsCount = 0; + USHORT k, j; + + static DWORD size; + static PBYTE pData; + + if (pData == NULL) { + if ((pData = malloc(PROPERTY_BUFFER)) != NULL) + size = PROPERTY_BUFFER; + } + + /* Get the size of the array (if the property is an array) */ + GetArraySize(pEvent, pInfo, i, &ArraySize); + + for (k = 0; k < ArraySize; k++) { + wprintf(L"%*s%s: ", (pStructureName) ? 4 : 0, L"", (LPWSTR) + ((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset)); + + /* If the property is a structure, print the members of the structure */ + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct) { + printf("\n"); + + LastMember = + pInfo->EventPropertyInfoArray[i].structType.StructStartIndex + + pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers; + + for (j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++) { + sts = PrintProperties(pEvent, pInfo, j, +(LPWSTR)((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset), k); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, + "Printing the members of the structure failed\n"); + goto cleanup; + } + } + } else { + ZeroMemory(&DataDescriptors, sizeof(DataDescriptors)); + + /* + * To retrieve a member of a structure, you need to specify + * an array of descriptors. The first descriptor in the array + * identifies the name of the structure and the second + * descriptor defines the member of the structure whose data + * you want to retrieve. + */ + if (pStructureName) { + DataDescriptors[0].PropertyName = (ULONGLONG)pStructureName; + DataDescriptors[0].ArrayIndex = StructIndex; + DataDescriptors[1].PropertyName = (ULONGLONG) + ((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset); + DataDescriptors[1].ArrayIndex = k; + DescriptorsCount = 2; + } else { + DataDescriptors[0].PropertyName = (ULONGLONG) + ((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset); + DataDescriptors[0].ArrayIndex = k; + DescriptorsCount = 1; + } + + /* + * TDH API does not support IPv6 addresses. + * If the output type is TDH_OUTTYPE_IPV6, you will be unable + * to consume the rest of the event. + * If you try to consume the remainder of the event, you will + * get ERROR_EVT_INVALID_EVENT_DATA. + */ + if (TDH_INTYPE_BINARY == + pInfo->EventPropertyInfoArray[i].nonStructType.InType && + TDH_OUTTYPE_IPV6 == + pInfo->EventPropertyInfoArray[i].nonStructType.OutType) { + fprintf(stderr, "Event contains an IPv6 address. Skipping.\n"); + sts = ERROR_EVT_INVALID_EVENT_DATA; + break; + } else { +retry: + sts = TdhGetProperty(pEvent, 0, NULL, + DescriptorsCount, &DataDescriptors[0], size, pData); + if (sts == ERROR_INSUFFICIENT_BUFFER) { + /* TdhGetPropertySize failing on Win2008, so do this: */ + pData = realloc(pData, size *= 2); + goto retry; + } else if (sts != ERROR_SUCCESS) { + fprintf(stderr, "TdhGetProperty failed: %s (%ld)\n", + tdherror(sts), sts); + goto cleanup; + } + + /* + * Get the name/value map if the property specifies a value map. + */ + sts = GetMapInfo(pEvent, (PWCHAR) ((PBYTE)(pInfo) + + pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset), + pInfo->DecodingSource, + pMapInfo); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, "GetMapInfo failed\n"); + goto cleanup; + } + + sts = FormatAndPrintData(pEvent, + pInfo->EventPropertyInfoArray[i].nonStructType.InType, + pInfo->EventPropertyInfoArray[i].nonStructType.OutType, + pData, size, pMapInfo); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, "FormatAndPrintData failed\n"); + goto cleanup; + } + + if (pMapInfo) { + free(pMapInfo); + pMapInfo = NULL; + } + } + } + } + +cleanup: + + if (pMapInfo) { + free(pMapInfo); + pMapInfo = NULL; + } + + return sts; +} + +void +PrintHeader(PEVENT_RECORD pEvent) +{ + /* Note: EventHeader.ProcessId is defined as ULONG */ + printf("Event HEADER (size=%u) flags=%s type=%s\npid=%lu tid=%ld eid=%u\n", + pEvent->EventHeader.Size, + eventHeaderFlags(pEvent->EventHeader.Flags), + eventPropertyFlags(pEvent->EventHeader.EventProperty), + pEvent->EventHeader.ThreadId, pEvent->EventHeader.ProcessId, + pEvent->EventHeader.EventDescriptor.Id); + if (pEvent->EventHeader.Flags & + (EVENT_HEADER_FLAG_PRIVATE_SESSION|EVENT_HEADER_FLAG_NO_CPUTIME)) { + printf("Time processor=%"PRIu64"\n", pEvent->EventHeader.ProcessorTime); + } else { + printf("Time: sys=%lu usr=%lu\n", + pEvent->EventHeader.KernelTime, pEvent->EventHeader.UserTime); + } + printf("Event PROVIDER %s\n", strguid(&pEvent->EventHeader.ProviderId)); + printf("Event ACTIVITY %s\n", strguid(&pEvent->EventHeader.ActivityId)); +} + +void +PrintTimestamp(PEVENT_RECORD pEvent) +{ + ULONGLONG TimeStamp = 0; + ULONGLONG Nanoseconds = 0; + SYSTEMTIME st; + SYSTEMTIME stLocal; + FILETIME ft; + + /* Print the time stamp for when the event occurred */ + ft.dwHighDateTime = pEvent->EventHeader.TimeStamp.HighPart; + ft.dwLowDateTime = pEvent->EventHeader.TimeStamp.LowPart; + + FileTimeToSystemTime(&ft, &st); + SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal); + TimeStamp = pEvent->EventHeader.TimeStamp.QuadPart; + Nanoseconds = (TimeStamp % 10000000) * 100; + + printf("Event TIMESTAMP: %02d/%02d/%02d %02d:%02d:%02d.%I64u\n", + stLocal.wMonth, stLocal.wDay, stLocal.wYear, stLocal.wHour, + stLocal.wMinute, stLocal.wSecond, Nanoseconds); +} + +void +PrintEventInfo(PTRACE_EVENT_INFO pInfo) +{ + if (DecodingSourceWbem == pInfo->DecodingSource) + printf("EventInfo: MOF class event\n"); + else if (DecodingSourceXMLFile == pInfo->DecodingSource) + printf("EventInfo: XML manifest event\n"); + else if (DecodingSourceWPP == pInfo->DecodingSource) + printf("EventInfo: WPP event\n"); + + printf("Event GUID: %s\n", strguid(&pInfo->EventGuid)); + + if (pInfo->ProviderNameOffset > 0) + wprintf(L"Provider name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->ProviderNameOffset)); + + if (DecodingSourceXMLFile == pInfo->DecodingSource) + wprintf(L"Event ID: %hu\n", pInfo->EventDescriptor.Id); + + wprintf(L"Version: %d\n", pInfo->EventDescriptor.Version); + + if (pInfo->ChannelNameOffset > 0) + wprintf(L"Channel name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->ChannelNameOffset)); + + if (pInfo->LevelNameOffset > 0) + wprintf(L"Level name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->LevelNameOffset)); + else + wprintf(L"Level: %hu\n", pInfo->EventDescriptor.Level); + + if (DecodingSourceXMLFile == pInfo->DecodingSource) { + if (pInfo->OpcodeNameOffset > 0) + wprintf(L"Opcode name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->OpcodeNameOffset)); + } else + wprintf(L"Type: %hu\n", pInfo->EventDescriptor.Opcode); + + if (DecodingSourceXMLFile == pInfo->DecodingSource) { + if (pInfo->TaskNameOffset > 0) + wprintf(L"Task name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->TaskNameOffset)); + } else + wprintf(L"Task: %hu\n", pInfo->EventDescriptor.Task); + + wprintf(L"Keyword mask: 0x%x\n", pInfo->EventDescriptor.Keyword); + if (pInfo->KeywordsNameOffset) { + LPWSTR pKeyword = (LPWSTR)((PBYTE)(pInfo) + pInfo->KeywordsNameOffset); + + for (; *pKeyword != 0; pKeyword += (wcslen(pKeyword) + 1)) + wprintf(L" Keyword name: %s\n", pKeyword); + } + + if (pInfo->EventMessageOffset > 0) + wprintf(L"Event message: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->EventMessageOffset)); + + if (pInfo->ActivityIDNameOffset > 0) + wprintf(L"Activity ID name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->ActivityIDNameOffset)); + + if (pInfo->RelatedActivityIDNameOffset > 0) + wprintf(L"Related activity ID name: %s\n", + (LPWSTR)((PBYTE)(pInfo) + pInfo->RelatedActivityIDNameOffset)); +} + +/* Callback that receives the events */ +VOID WINAPI +ProcessEvent(PEVENT_RECORD pEvent) +{ + DWORD sts = ERROR_SUCCESS; + PTRACE_EVENT_INFO pInfo = NULL; + USHORT i; + + PrintHeader(pEvent); + PrintTimestamp(pEvent); + + /* + * Process the event. + * The pEvent->UserData member is a pointer to the event specific data, + * if any exists. + */ + sts = GetEventInformation(pEvent, &pInfo); + if (sts != ERROR_SUCCESS) + goto cleanup; + + PrintEventInfo(pInfo); + + if (DecodingSourceWPP == pInfo->DecodingSource) + /* Not handling the WPP case, unless just an inline string */ + if (!(pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_STRING_ONLY)) + goto cleanup; + + /* + * Print the event data for all the top-level properties. + * Metadata for all the top-level properties comes before structure + * member properties in the property information array. + * If the EVENT_HEADER_FLAG_STRING_ONLY flag is set, the event data + * is a null-terminated string, so just print it. + */ + if (EVENT_HEADER_FLAG_STRING_ONLY == + (pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_STRING_ONLY)) { + printf("Embedded: %s\n", (char *)pEvent->UserData); + } else { + for (i = 0; i < pInfo->TopLevelPropertyCount; i++) { + sts = PrintProperties(pEvent, pInfo, i, NULL, 0); + if (sts != ERROR_SUCCESS) { + fprintf(stderr, + "Printing top level properties failed, property %u.\n", + i); + } + } + } + +cleanup: + fflush(stdout); + if (pInfo) + free(pInfo); +} + +static struct { + EVENT_TRACE_PROPERTIES *properties; + TRACEHANDLE session; + LPTSTR name; +} sessions[MAX_SESSIONS]; +static int sCount; + +static void +stopSession(TRACEHANDLE session, LPTSTR name, + PEVENT_TRACE_PROPERTIES properties) +{ + if (session != INVALID_PROCESSTRACE_HANDLE) { + ULONG sts = ControlTrace(session, name, properties, + EVENT_TRACE_CONTROL_STOP); + if (sts != ERROR_SUCCESS) + fprintf(stderr, "ControlTrace failed (%s): %s\n", name, + tdherror(sts)); + else + fprintf(stderr, "Stopped %s event tracing session\n", name); + } +} + +static void __attribute__((constructor)) initTracing() +{ + int i; + + for (i = 0; i < sizeof(sessions) / sizeof(sessions[0]); i++) + sessions[i].session = INVALID_PROCESSTRACE_HANDLE; +} + +static void __attribute__((destructor)) stopTracing() +{ + int i; + + for (i = 0; i < sCount; i++) { + sessions[i].properties->EnableFlags = 0; + stopSession(sessions[i].session, sessions[i].name, sessions[i].properties); + } +} + +void +enableEventTrace(LPTSTR name, ULONG namelen, const GUID *guid, + ULONG enableFlags, BOOL useGlobalSequence) +{ + TRACEHANDLE session; + EVENT_TRACE_PROPERTIES *properties; + ULONG size, sts = ERROR_SUCCESS; + int retryStartTrace = 0; + + size = sizeof(EVENT_TRACE_PROPERTIES) + namelen; + properties = malloc(size); + if (properties == NULL) { + fprintf(stderr, "Insufficient memory: %lu bytes\n", size); + exit(1); + } + + sessions[sCount].properties = properties; + sessions[sCount].session = session; + sessions[sCount].name = name; + sCount++; + +retrySession: + ZeroMemory(properties, size); + properties->Wnode.BufferSize = size; + properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + properties->Wnode.ClientContext = 1; + properties->Wnode.Guid = *guid; + properties->EnableFlags = enableFlags; + properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + if (useGlobalSequence == TRUE) + properties->LogFileMode |= EVENT_TRACE_USE_GLOBAL_SEQUENCE; + properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + + sts = StartTrace(&session, name, properties); + if (sts != ERROR_SUCCESS) { + if (retryStartTrace == 1) { + fprintf(stderr, "Cannot start %s session (in use)\n", name); + } else if (sts == ERROR_ALREADY_EXISTS) { + fprintf(stderr, "%s session is in use - retry ... (flags=%lx)\n", + name, enableFlags); + stopTracing(); + retryStartTrace = 1; + goto retrySession; + } + else { + fprintf(stderr, "StartTrace: %s\n", tdherror(sts)); + } + exit(1); + } +} + +ULONG bufferCount, buffersRead, bufferTotal, bufferBytes, eventsLost, sysCount; + +ULONG WINAPI +BufferCallback(PEVENT_TRACE_LOGFILE mode) +{ + buffersRead += mode->BuffersRead; + bufferTotal += mode->BufferSize; + bufferBytes += mode->Filled; + eventsLost += mode->EventsLost; + sysCount += mode->IsKernelTrace; + bufferCount++; + printf("Event BUFFER (size=%lu) all reads=%lu bytes=%lu lost=%lu sys=%lu\n", + mode->Filled, buffersRead, bufferBytes, eventsLost, sysCount); + return TRUE; +} + +LPGUID +LookupGuidInBuffer(LPTSTR name, PPROVIDER_ENUMERATION_INFO buffer) +{ + PTRACE_PROVIDER_INFO traceProviderInfo; + PWCHAR stringPointer; + char s[1024]; + LPGUID guidPointer; + PBYTE bufferPointer = (PBYTE)buffer; + ULONG i; + + for (i = 0; i < buffer->NumberOfProviders; i++) { + traceProviderInfo = &buffer->TraceProviderInfoArray[i]; + if (traceProviderInfo->ProviderNameOffset == 0) + continue; + guidPointer = &traceProviderInfo->ProviderGuid; + stringPointer = (PWCHAR) + (bufferPointer + traceProviderInfo->ProviderNameOffset); + WideCharToMultiByte(CP_ACP, 0, stringPointer, -1, s, 1024, NULL, NULL); + if (strcmp(s, name) == 0) + return guidPointer; + } + return NULL; +} + +ULONG +ProviderGuid(LPTSTR name, LPGUID guidPointer) +{ + PROVIDER_ENUMERATION_INFO providerEnumerationInfo; + PPROVIDER_ENUMERATION_INFO buffer; + ULONG size, sts; + + buffer = &providerEnumerationInfo; + size = sizeof(providerEnumerationInfo); + sts = TdhEnumerateProviders(buffer, &size); + do { + if (sts == ERROR_INSUFFICIENT_BUFFER) { + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + buffer = (PPROVIDER_ENUMERATION_INFO)BufferAllocate(size); + if (!buffer) + return ERROR_NOT_ENOUGH_MEMORY; + sts = TdhEnumerateProviders(buffer, &size); + } + else if (sts == ERROR_SUCCESS) { + guidPointer = LookupGuidInBuffer(name, buffer); + break; + } + else { + fprintf(stderr, "TdhEnumerateProviders failed: %s (=%lu)\n", + tdherror(sts), sts); + break; + } + } while (1); + + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + + return sts; +} + +TRACEHANDLE +openTraceHandle(LPTSTR name) +{ + TRACEHANDLE handle; + EVENT_TRACE_LOGFILE traceMode; + + ZeroMemory(&traceMode, sizeof(EVENT_TRACE_LOGFILE)); + traceMode.LoggerName = name; + traceMode.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACK)BufferCallback; + traceMode.EventRecordCallback = (PEVENT_RECORD_CALLBACK)ProcessEvent; + traceMode.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME; + + handle = OpenTrace(&traceMode); + if (handle == INVALID_PROCESSTRACE_HANDLE) { + ULONG sts = GetLastError(); + fprintf(stderr, "OpenTrace: %s (%lu)\n", tdherror(sts), sts); + CloseTrace(handle); + exit(1); + } + return handle; +} + +static char *options = "k:v?"; +static char usage[] = + "Usage: %s [options] tracename\n\n" + "Options:\n" + " -k subsys kernel subsystem to trace\n" + " -g use global sequence numbers\n" + " -v verbose diagnostics (errors)\n"; + +int +main(int argc, LPTSTR *argv) +{ + BOOL useGlobalSequence = FALSE; + TRACEHANDLE session; + ULONG sts, sysFlags = 0; + int c; + + while ((c = getopt(argc, argv, options)) != EOF) { + switch (c) { + case 'g': + useGlobalSequence = TRUE; + break; + case 'k': + sysFlags |= kernelTraceFlag(optarg); + break; + case 'v': + verbose++; + break; + case '?': + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + } + + if (sysFlags) { + enableEventTrace(KERNEL_LOGGER_NAME, sizeof(KERNEL_LOGGER_NAME), + &SystemTraceControlGuid, sysFlags, useGlobalSequence); + session = openTraceHandle(KERNEL_LOGGER_NAME); + } else { + session = openTraceHandle(PCP_SESSION); + } + + sts = ProcessTrace(&session, 1, NULL, NULL); + if (sts == ERROR_CANCELLED) + sts = ERROR_SUCCESS; + if (sts != ERROR_SUCCESS) + fprintf(stderr, "ProcessTrace: %s (%lu)\n", tdherror(sts), sts); + return sts; +} diff --git a/src/pmdas/etw/tdhlist.c b/src/pmdas/etw/tdhlist.c new file mode 100644 index 0000000..e21c024 --- /dev/null +++ b/src/pmdas/etw/tdhlist.c @@ -0,0 +1,267 @@ +/* + * List Event Traces Providers or sessions for Windows events. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "util.h" + +#define MAX_SESSIONS (ULONG)64 +#define MAX_SESSION_NAME_LEN 1024 +#define MAX_LOGFILE_PATH_LEN 1024 + +void +PrintFieldInfo(PBYTE buffer, + PPROVIDER_FIELD_INFO fieldInfo, int id, EVENT_FIELD_TYPE eventFieldType) +{ + PWCHAR stringBuffer; + + printf("\tField %u\n", id); + switch (eventFieldType) { + case EventKeywordInformation: + printf("\t\tType: KeywordInformation\n"); + break; + case EventLevelInformation: + printf("\t\tType: LevelInformation\n"); + break; + case EventChannelInformation: + printf("\t\tType: ChannelInformation\n"); + break; + case EventTaskInformation: + printf("\t\tType: TaskInformation\n"); + break; + case EventOpcodeInformation: + printf("\t\tType: OpcodeInformation\n"); + break; + default: + break; + } + printf("\t\tValue: %" PRIu64 "\n", fieldInfo->Value); + if (fieldInfo->NameOffset) { + stringBuffer = (PWCHAR)(buffer + fieldInfo->NameOffset); + printf("\t\tField: %ls\n", stringBuffer); + } + if (fieldInfo->DescriptionOffset) { + stringBuffer = (PWCHAR)(buffer + fieldInfo->DescriptionOffset); + printf("\t\tDescription: %ls\n", stringBuffer); + } +} + +void +PrintFieldElements(PPROVIDER_FIELD_INFOARRAY buffer, EVENT_FIELD_TYPE eventFieldType) +{ + PPROVIDER_FIELD_INFO traceFieldInfo; + ULONG i; + + for (i = 0; i < buffer->NumberOfElements; i++) { + traceFieldInfo = &buffer->FieldInfoArray[i]; + PrintFieldInfo((PBYTE)buffer, traceFieldInfo, i, eventFieldType); + } +} + +void +EnumerateProviderFieldInformation(LPGUID guidPointer, EVENT_FIELD_TYPE eventFieldType) +{ + PPROVIDER_FIELD_INFOARRAY buffer = NULL; + ULONG sts, size = 0; + + sts = TdhEnumerateProviderFieldInformation(guidPointer, + eventFieldType, buffer, &size); + do { + if (sts == ERROR_INSUFFICIENT_BUFFER) { + BufferFree(buffer); + buffer = (PPROVIDER_FIELD_INFOARRAY)BufferAllocate(size); + if (!buffer) + return; + sts = TdhEnumerateProviderFieldInformation(guidPointer, + eventFieldType, buffer, &size); + } + else if (sts == ERROR_SUCCESS) { + PrintFieldElements(buffer, eventFieldType); + break; + } + else if (sts == ERROR_NOT_FOUND) { + break; + } + else { + fprintf(stderr, "TdhEnumerateProviderFieldInformation: %s (%lu)\n", + tdherror(sts), sts); + break; + } + } while (1); + + BufferFree(buffer); +} + +void +PrintTraceProviderInfo(PBYTE buffer, PTRACE_PROVIDER_INFO traceProviderInfo) +{ + LPGUID guidPointer = &traceProviderInfo->ProviderGuid; + PWCHAR stringBuffer; + ULONG i; + + if (traceProviderInfo->ProviderNameOffset) { + stringBuffer = (PWCHAR)(buffer + traceProviderInfo->ProviderNameOffset); + printf("Name: %ls\n", stringBuffer); + } + + printf("Guid: %s\n", strguid(guidPointer)); + printf("SchemaSource: %ld (%s)\n", traceProviderInfo->SchemaSource, + traceProviderInfo->SchemaSource==0 ? "XML manifest" : "WMI MOF class"); + + for (i = EventKeywordInformation; i <= EventChannelInformation; i++) { + EnumerateProviderFieldInformation(guidPointer, (EVENT_FIELD_TYPE)i); + } +} + +void +PrintProviderEnumeration(PPROVIDER_ENUMERATION_INFO buffer) +{ + PTRACE_PROVIDER_INFO traceProviderInfo; + ULONG i; + + for (i = 0; i < buffer->NumberOfProviders; i++) { + traceProviderInfo = &buffer->TraceProviderInfoArray[i]; + PrintTraceProviderInfo((PBYTE)buffer, traceProviderInfo); + } +} + +ULONG +EnumerateProviders(void) +{ + PROVIDER_ENUMERATION_INFO providerEnumerationInfo; + PPROVIDER_ENUMERATION_INFO buffer; + ULONG size, sts; + + buffer = &providerEnumerationInfo; + size = sizeof(providerEnumerationInfo); + sts = TdhEnumerateProviders(buffer, &size); + do { + if (sts == ERROR_INSUFFICIENT_BUFFER) { + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + buffer = (PPROVIDER_ENUMERATION_INFO)BufferAllocate(size); + if (!buffer) + return ERROR_NOT_ENOUGH_MEMORY; + sts = TdhEnumerateProviders(buffer, &size); + } + else if (sts == ERROR_SUCCESS) { + PrintProviderEnumeration(buffer); + break; + } + else { + fprintf(stderr, "TdhEnumerateProviders failed: %s (=%lu)\n", + tdherror(sts), sts); + break; + } + } while (1); + + if (buffer != &providerEnumerationInfo) + BufferFree(buffer); + + return sts; +} + +ULONG +EnumerateSessions(void) +{ + PEVENT_TRACE_PROPERTIES pSessions[MAX_SESSIONS]; + PEVENT_TRACE_PROPERTIES pBuffer = NULL; + ULONG PropertiesSize = 0; + ULONG SessionCount = 0; + ULONG BufferSize = 0; + ULONG i, sts = ERROR_SUCCESS; + + PropertiesSize = sizeof(EVENT_TRACE_PROPERTIES) + + (MAX_SESSION_NAME_LEN * sizeof(WCHAR)) + + (MAX_LOGFILE_PATH_LEN * sizeof(WCHAR)); + BufferSize = PropertiesSize * MAX_SESSIONS; + + pBuffer = (PEVENT_TRACE_PROPERTIES) malloc(BufferSize); + if (pBuffer) { + ZeroMemory(pBuffer, BufferSize); + for (i = 0; i < MAX_SESSIONS; i++) { + pSessions[i] = (EVENT_TRACE_PROPERTIES *)((BYTE *)pBuffer + + (i * PropertiesSize)); + pSessions[i]->Wnode.BufferSize = PropertiesSize; + pSessions[i]->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + pSessions[i]->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + + (MAX_SESSION_NAME_LEN * sizeof(WCHAR)); + } + } else { + fprintf(stderr, "Error allocating memory for properties.\n"); + return ENOMEM; + } + + sts = QueryAllTraces(pSessions, MAX_SESSIONS, &SessionCount); + if (sts == ERROR_SUCCESS || sts == ERROR_MORE_DATA) { + printf("Requested session count, %ld. Actual session count, %ld.\n\n", + MAX_SESSIONS, SessionCount); + for (i = 0; i < SessionCount; i++) { + LPCSTR l, f; + l = (LPCSTR)((char *)pSessions[i] + pSessions[i]->LoggerNameOffset); + f = (LPCSTR)((char*)pSessions[i] + pSessions[i]->LogFileNameOffset); + printf("Session GUID: %s\nSession ID: %"PRIu64"\n", + strguid(&pSessions[i]->Wnode.Guid), + pSessions[i]->Wnode.HistoricalContext); + if (pSessions[i]->LogFileNameOffset == 0) + printf("Realtime session name: %s\n", l); + else + printf("Log session name: %s\nLog file: %s\n", l, f); + if (memcmp(&SystemTraceControlGuid, + &pSessions[i]->Wnode.Guid, sizeof(GUID)) == 0) { + printf("Enable Flags: "); + dumpKernelTraceFlags(stdout, "", ","); + printf("\n"); + } + printf("flush timer: %ld\n" + "min buffers: %ld\n" + "max buffers: %ld\n" + "buffers: %ld\n" + "buffers written: %ld\n" + "buffers lost: %ld\n" + "events lost: %ld\n\n", + pSessions[i]->FlushTimer, + pSessions[i]->MinimumBuffers, + pSessions[i]->MaximumBuffers, + pSessions[i]->NumberOfBuffers, + pSessions[i]->BuffersWritten, + pSessions[i]->LogBuffersLost, + pSessions[i]->EventsLost); + } + } else { + fprintf(stderr, "Error calling QueryAllTraces: %s (%ld)\n", + tdherror(sts), sts); + } + + free(pBuffer); + return sts; +} + +int +main(int argc, char **argv) +{ + ULONG sts; + + if (argc > 1 && strcmp(argv[1], "-s") == 0) + sts = EnumerateSessions(); + else + sts = EnumerateProviders(); + return sts; +} diff --git a/src/pmdas/etw/util.c b/src/pmdas/etw/util.c new file mode 100644 index 0000000..19869e6 --- /dev/null +++ b/src/pmdas/etw/util.c @@ -0,0 +1,190 @@ +/* + * Event Trace for Windows utility routines. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define INITGUID +#include +#include +#include +#include +#include "util.h" + +struct { + ULONG flag; + char *name; +} kernelFlags[] = { + { EVENT_TRACE_FLAG_PROCESS, "process" }, + { EVENT_TRACE_FLAG_THREAD, "thread" }, + { EVENT_TRACE_FLAG_IMAGE_LOAD, "image_load" }, + { EVENT_TRACE_FLAG_DISK_IO, "disk_io" }, + { EVENT_TRACE_FLAG_DISK_FILE_IO, "disk_file_io" }, + { EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS, "memory_page_faults" }, + { EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS, "memory_hard_faults" }, + { EVENT_TRACE_FLAG_NETWORK_TCPIP, "network_tcpip" }, + { EVENT_TRACE_FLAG_REGISTRY, "registry" }, + { EVENT_TRACE_FLAG_DBGPRINT, "dbgprint" }, + { EVENT_TRACE_FLAG_PROCESS_COUNTERS, "process_counters" }, + { EVENT_TRACE_FLAG_CSWITCH, "cswitch" }, + { EVENT_TRACE_FLAG_DPC, "dpc" }, + { EVENT_TRACE_FLAG_INTERRUPT, "interrupt" }, + { EVENT_TRACE_FLAG_SYSTEMCALL, "syscall" }, + { EVENT_TRACE_FLAG_DISK_IO_INIT, "disk_io_init" }, + { EVENT_TRACE_FLAG_ALPC, "alpc" }, + { EVENT_TRACE_FLAG_SPLIT_IO, "split_io" }, + { EVENT_TRACE_FLAG_DRIVER, "driver" }, + { EVENT_TRACE_FLAG_PROFILE, "profile" }, + { EVENT_TRACE_FLAG_FILE_IO, "file_io" }, + { EVENT_TRACE_FLAG_FILE_IO_INIT, "file_io_init" }, + { EVENT_TRACE_FLAG_DISPATCHER, "dispatcher" }, + { EVENT_TRACE_FLAG_VIRTUAL_ALLOC, "virtual_alloc" }, + { EVENT_TRACE_FLAG_EXTENSION, "extension" }, + { EVENT_TRACE_FLAG_FORWARD_WMI, "forward_wmi" }, + { EVENT_TRACE_FLAG_ENABLE_RESERVE, "enable_reserve" }, +}; + +void +dumpKernelTraceFlags(FILE *output, const char *prefix, const char *suffix) +{ + int i; + + for (i = 0; i < sizeof(kernelFlags)/sizeof(kernelFlags[0]); i++) + fprintf(output, "%s%s%s", prefix, kernelFlags[i].name, + (i+1 == sizeof(kernelFlags)/sizeof(kernelFlags[0])) ? + "\0" : suffix); +} + +ULONG +kernelTraceFlag(const char *name) +{ + int i; + + for (i = 0; i < sizeof(kernelFlags)/sizeof(kernelFlags[0]); i++) + if (strcmp(kernelFlags[i].name, name) == 0) + return kernelFlags[i].flag; + fprintf(stderr, "Unrecognised kernel trace flag: %s\n", name); + fprintf(stderr, "List of all known options:\n"); + dumpKernelTraceFlags(stderr, "\t", "\n"); + fprintf(stderr, "\n\n"); + exit(1); +} + +const char * +eventPropertyFlags(USHORT flags) +{ + if (flags & EVENT_HEADER_PROPERTY_XML) + return "XML"; + if (flags & EVENT_HEADER_PROPERTY_FORWARDED_XML) + return "forwarded XML"; + if (flags & EVENT_HEADER_PROPERTY_LEGACY_EVENTLOG) + return "legacy WMI MOF"; + return "none"; +} + +const char * +eventHeaderFlags(USHORT flags) +{ + static char buffer[128]; + char *p = &buffer[0]; + + *p = '\0'; + if (flags & EVENT_HEADER_FLAG_EXTENDED_INFO) + strcat(p, "extended info,"); + if (flags & EVENT_HEADER_FLAG_PRIVATE_SESSION) + strcat(p, "private session,"); + if (flags & EVENT_HEADER_FLAG_STRING_ONLY) + strcat(p, "string,"); + if (flags & EVENT_HEADER_FLAG_TRACE_MESSAGE) + strcat(p, "TraceMessage,"); + if (flags & EVENT_HEADER_FLAG_NO_CPUTIME) + strcat(p, "no cputime,"); + if (flags & EVENT_HEADER_FLAG_32_BIT_HEADER) + strcat(p, "32bit,"); + if (flags & EVENT_HEADER_FLAG_64_BIT_HEADER) + strcat(p, "64bit,"); + if (flags & EVENT_HEADER_FLAG_CLASSIC_HEADER) + strcat(p, "classic,"); + buffer[strlen(buffer)-1] = '\0'; + return buffer; +} + +const char * +tdherror(ULONG code) +{ + switch (code){ + case ERROR_ACCESS_DENIED: + return "Insufficient privileges for requested operation"; + case ERROR_ALREADY_EXISTS: + return "A sessions with the same name or GUID already exists"; + case ERROR_BAD_LENGTH: + return "Insufficient space or size for a parameter"; + case ERROR_BAD_PATHNAME: + return "Given path parameter is not valid"; + case ERROR_CANCELLED: + return "Consumer cancelled processing via buffer callback"; + case ERROR_FILE_NOT_FOUND: + return "Unable to find the requested file"; + case ERROR_INSUFFICIENT_BUFFER: + return "Size of buffer is too small"; + case ERROR_INVALID_HANDLE: + return "Element of array is not a valid event tracing session handle"; + case ERROR_INVALID_PARAMETER: + return "One or more of the parameters is not valid"; + case ERROR_INVALID_TIME: + return "EndTime is less than StartTime"; + case ERROR_NOACCESS: + return "An exception occurred in one of the event callback routines"; + case ERROR_NOT_FOUND: + return "Requested class or field type not found"; + case ERROR_NOT_SUPPORTED: + return "The requested field type is not supported"; + case ERROR_OUTOFMEMORY: + return "Insufficient memory"; + case ERROR_WMI_INSTANCE_NOT_FOUND: + return "Session from which realtime events to be consumed not running"; + case ERROR_WMI_ALREADY_ENABLED: + return "Handle array contains more than one realtime session handle"; + case ERROR_SUCCESS: + return "Success"; + } + return strerror(code); +} + +const char * +strguid(LPGUID guidPointer) +{ + static char stringBuffer[64]; + + snprintf(stringBuffer, sizeof(stringBuffer), + "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + guidPointer->Data1, guidPointer->Data2, guidPointer->Data3, + guidPointer->Data4[0], guidPointer->Data4[1], + guidPointer->Data4[2], guidPointer->Data4[3], + guidPointer->Data4[4], guidPointer->Data4[5], + guidPointer->Data4[6], guidPointer->Data4[7]); + return stringBuffer; +} + +void * +BufferAllocate(ULONG size) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); +} + +void +BufferFree(void *buffer) +{ + if (buffer) + HeapFree(GetProcessHeap(), 0, buffer); +} diff --git a/src/pmdas/etw/util.h b/src/pmdas/etw/util.h new file mode 100644 index 0000000..4c3b32a --- /dev/null +++ b/src/pmdas/etw/util.h @@ -0,0 +1,31 @@ +/* + * Trace Data Helper utility routines. + * + * Copyright (c) 2011, Nathan Scott. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef UTIL_H +#define UTIL_H + +extern void dumpKernelTraceFlags(FILE *, const char *, const char *); +extern ULONG kernelTraceFlag(const char *); + +extern const char *eventHeaderFlags(USHORT); +extern const char *eventPropertyFlags(USHORT); + +extern const char *strguid(LPGUID); +extern const char *tdherror(ULONG); + +extern void *BufferAllocate(ULONG); +extern void BufferFree(void *); + +#endif diff --git a/src/pmdas/freebsd/GNUmakefile b/src/pmdas/freebsd/GNUmakefile new file mode 100644 index 0000000..5aa647d --- /dev/null +++ b/src/pmdas/freebsd/GNUmakefile @@ -0,0 +1,69 @@ +# +# Copyright (c) 2000,2003,2004,2008 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2007-2010 Aconex. All Rights Reserved. +# Copyright (c) 2012 Ken McDonell. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = freebsd +DOMAIN = FREEBSD +CMDTARGET = pmdafreebsd +LIBTARGET = pmda_freebsd.so +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "freebsd 85 dso freebsd_init $(PMDADIR)/$(LIBTARGET)" + +CFILES = freebsd.c disk.c netif.c + +HFILES = freebsd.h + +LSRCFILES = help root_freebsd +LDIRT = help.dir help.pag help.sed help.tmp domain.h $(IAM).log + +LLDLIBS = $(PCP_PMDALIB) -ldevstat + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "freebsd" +build-me: domain.h $(LIBTARGET) $(CMDTARGET) help.dir help.pag + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help help.dir help.pag $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root_freebsd $(PCP_VAR_DIR)/pmns/root_freebsd +else +build-me: +install: +endif + +help.dir help.pag : domain.h help + $(SED) help.sed -n -e '/#define/s/#define[ ]*\([A-Z][A-Z]*\)[ ]*\([0-9][0-9]*\)/s@\1@\2@/p' + $(SED) -f help.sed help.tmp + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_freebsd -v 2 -o help +#include "freebsd.h" + +struct devinfo devinfo = { 0 }; +struct statinfo statinfo; + +void +refresh_disk_metrics(void) +{ + static int init_done = 0; + int i; + int sts; + + if (!init_done) { + sts = devstat_checkversion(NULL); + if (sts != 0) { + fprintf(stderr, "refresh_disk_metrics: devstat_checkversion: failed! %s\n", devstat_errbuf); + exit(1); + } + statinfo.dinfo = &devinfo; + init_done = 1; + } + + sts = devstat_getdevs(NULL, &statinfo); + if (sts < 0) { + fprintf(stderr, "refresh_disk_metrics: devstat_getdevs: %s\n", strerror(errno)); + exit(1); + } + else if (sts == 1) { + /* + * First call, else devstat[] list has changed + */ + struct devstat *dsp; + char iname[DEVSTAT_NAME_LEN+6]; + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_INACTIVE); + for (i = 0; i < devinfo.numdevs; i++) { + dsp = &devinfo.devices[i]; + /* + * Skip entries that are not interesting ... only include + * "da" (direct access) disks at this stage + */ + if (strcmp(dsp->device_name, "da") != 0) + continue; + snprintf(iname, sizeof(iname), "%s%d", dsp->device_name, dsp->unit_number); + sts = pmdaCacheLookupName(indomtab[DISK_INDOM].it_indom, iname, NULL, NULL); + if (sts == PMDA_CACHE_ACTIVE) { + int j; + fprintf(stderr, "refresh_disk_metrics: Warning: duplicate name (%s) in disk indom\n", iname); + for (j = 0; j < devinfo.numdevs; j++) { + dsp = &devinfo.devices[j]; + fprintf(stderr, " devinfo[%d]: %s%d\n", j, dsp->device_name, dsp->unit_number); + } + continue; + } + else { + /* new entry or reactivate an existing one */ + pmdaCacheStore(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_ADD, iname, (void *)dsp); + } + } + } + +} + +int +do_disk_metrics(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + struct devstat *dsp; + int sts; + + if (inst != PM_IN_NULL) { + /* + * per-disk metrics + */ + sts = pmdaCacheLookup(indomtab[DISK_INDOM].it_indom, inst, NULL, (void **)&dsp); + if (sts == PMDA_CACHE_ACTIVE) { + sts = 1; + /* cluster and domain already checked, just need item ... */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 0: /* disk.dev.read */ + atom->ull = dsp->operations[DEVSTAT_READ]; + break; + + case 1: /* disk.dev.write */ + atom->ull = dsp->operations[DEVSTAT_WRITE]; + break; + + case 2: /* disk.dev.total */ + atom->ull = dsp->operations[DEVSTAT_READ] + dsp->operations[DEVSTAT_WRITE]; + break; + + case 3: /* disk.dev.read_bytes */ + atom->ull = dsp->bytes[DEVSTAT_READ]; + break; + + case 4: /* disk.dev.write_bytes */ + atom->ull = dsp->bytes[DEVSTAT_WRITE]; + break; + + case 5: /* disk.dev.total_bytes */ + atom->ull = dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]; + break; + + case 12: /* disk.dev.blkread */ + atom->ull = dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_READ] / dsp->block_size; + break; + + case 13: /* disk.dev.blkwrite */ + atom->ull = dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_WRITE] / dsp->block_size; + break; + + case 14: /* disk.dev.blktotal */ + atom->ull = dsp->block_size == 0 ? 0 : (dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]) / dsp->block_size; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else + sts = 0; + } + else { + /* + * all-disk summary metrics + */ + int i; + atom->ull = 0; + sts = 1; + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_WALK_REWIND); + while (sts == 1 && (i = pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_WALK_NEXT)) >= 0) { + int lsts; + lsts = pmdaCacheLookup(indomtab[DISK_INDOM].it_indom, i, NULL, (void **)&dsp); + if (lsts == PMDA_CACHE_ACTIVE) { + /* cluster and domain already checked, just need item ... */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 6: /* disk.all.read */ + atom->ull += dsp->operations[DEVSTAT_READ]; + break; + + case 7: /* disk.all.write */ + atom->ull += dsp->operations[DEVSTAT_WRITE]; + break; + + case 8: /* disk.all.total */ + atom->ull += dsp->operations[DEVSTAT_READ] + dsp->operations[DEVSTAT_WRITE]; + break; + + case 9: /* disk.all.read_bytes */ + atom->ull += dsp->bytes[DEVSTAT_READ]; + break; + + case 10: /* disk.all.write_bytes */ + atom->ull += dsp->bytes[DEVSTAT_WRITE]; + break; + + case 11: /* disk.all.total_bytes */ + atom->ull += dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]; + break; + + case 15: /* disk.all.blkread */ + atom->ull += dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_READ] / dsp->block_size; + break; + + case 16: /* disk.all.blkwrite */ + atom->ull += dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_WRITE] / dsp->block_size; + break; + + case 17: /* disk.all.blktotal */ + atom->ull += dsp->block_size == 0 ? 0 : (dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]) / dsp->block_size; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + } + if (i < 0 && i != -1) + /* not end of indom from cache walk, some other error */ + sts = i; + } + + return sts; +} diff --git a/src/pmdas/freebsd/freebsd.c b/src/pmdas/freebsd/freebsd.c new file mode 100644 index 0000000..3170b20 --- /dev/null +++ b/src/pmdas/freebsd/freebsd.c @@ -0,0 +1,991 @@ +/* + * FreeBSD Kernel PMDA + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2012 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "domain.h" +#include "freebsd.h" + +/* static instances */ +static pmdaInstid loadav_indom[] = { + { 1, "1 minute" }, { 5, "5 minute" }, { 15, "15 minute" } +}; + +/* instance domains */ +pmdaIndom indomtab[] = { + { LOADAV_INDOM, sizeof(loadav_indom)/sizeof(loadav_indom[0]), loadav_indom }, + { CPU_INDOM, 0, NULL }, + { DISK_INDOM, 0, NULL }, + { NETIF_INDOM, 0, NULL }, +}; +static int indomtablen = sizeof(indomtab) / sizeof(indomtab[0]); + +#define CL_SYSCTL 0 +#define CL_SPECIAL 1 +#define CL_DISK 2 +#define CL_NETIF 3 + +/* + * All the PCP metrics. + * + * For sysctl metrics, m_user (the first field) is set the the PCP + * name of the metric, and during initialization this is replaced by + * a pointer to the corresponding entry in mib[] (based on matching, + * or prefix matching (see matchname()) the PCP name here with + * m_pcpname[] in the mib[] entries. + * + * cluster map + * CL_SYSCTL simple sysctl() metrics, either one metric per mib, or + * one struct per mib + * CL_SPECIAL trickier sysctl() metrics involving synthesis or arithmetic + * or other methods + * CL_DISK disk metrics + * CL_NETIF network interface metrics + */ + +static pmdaMetric metrictab[] = { + { (void *)"hinv.ncpu", + { PMDA_PMID(CL_SYSCTL,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"hinv.physmem", + { PMDA_PMID(CL_SYSCTL,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { (void *)"kernel.all.load", + { PMDA_PMID(CL_SYSCTL,2), PM_TYPE_FLOAT, LOADAV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"kernel.all.cpu.user", + { PMDA_PMID(CL_SYSCTL,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.nice", + { PMDA_PMID(CL_SYSCTL,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.sys", + { PMDA_PMID(CL_SYSCTL,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.intr", + { PMDA_PMID(CL_SYSCTL,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.idle", + { PMDA_PMID(CL_SYSCTL,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.user", + { PMDA_PMID(CL_SYSCTL,8), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.nice", + { PMDA_PMID(CL_SYSCTL,9), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.sys", + { PMDA_PMID(CL_SYSCTL,10), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.intr", + { PMDA_PMID(CL_SYSCTL,11), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.idle", + { PMDA_PMID(CL_SYSCTL,12), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.hz", + { PMDA_PMID(CL_SYSCTL,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) } }, + { (void *)"hinv.cpu.vendor", + { PMDA_PMID(CL_SYSCTL,15), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"hinv.cpu.model", + { PMDA_PMID(CL_SYSCTL,16), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"hinv.cpu.arch", + { PMDA_PMID(CL_SYSCTL,17), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"swap.pagesin", + { PMDA_PMID(CL_SYSCTL,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.pagesout", + { PMDA_PMID(CL_SYSCTL,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.in", + { PMDA_PMID(CL_SYSCTL,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.in", + { PMDA_PMID(CL_SYSCTL,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"kernel.all.pswitch", + { PMDA_PMID(CL_SYSCTL,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"kernel.all.syscall", + { PMDA_PMID(CL_SYSCTL,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"kernel.all.intr", + { PMDA_PMID(CL_SYSCTL,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.length", + { PMDA_PMID(CL_SYSCTL,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { (void *)"swap.used", + { PMDA_PMID(CL_SYSCTL,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + { NULL, /* hinv.ndisk */ + { PMDA_PMID(CL_SPECIAL,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + /* + * swap.free is the difference between sysctl variables vm.swap_total + * and vm.swap_reserved, so it is important they are kept together + * in mib[] below + */ + { (void *)"swap.length", /* swap.free */ + { PMDA_PMID(CL_SPECIAL,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* hinv.pagesize */ + { PMDA_PMID(CL_SPECIAL,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { (void *)"mem.util.all", + { PMDA_PMID(CL_SPECIAL,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + /* + * mem.util.used is computed from several of the vm.stats.vm.v_*_count + * sysctl metrics, so it is important they are kept together in mib[] + * below + */ + { (void *)"mem.util.all", /* mem.util.used */ + { PMDA_PMID(CL_SPECIAL,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.free", + { PMDA_PMID(CL_SPECIAL,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.bufmem", + { PMDA_PMID(CL_SPECIAL,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.cached", + { PMDA_PMID(CL_SPECIAL,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.wired", + { PMDA_PMID(CL_SPECIAL,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.active", + { PMDA_PMID(CL_SPECIAL,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.inactive", + { PMDA_PMID(CL_SPECIAL,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + /* + * mem.util.avail is computed from several of the vm.stats.vm.v_*_count + * sysctl metrics, so it is important they are kept together in mib[] + * below + */ + { (void *)"mem.util.all", /* mem.util.avail */ + { PMDA_PMID(CL_SPECIAL,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + + { NULL, /* disk.dev.read */ + { PMDA_PMID(CL_DISK,0), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.write */ + { PMDA_PMID(CL_DISK,1), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.total */ + { PMDA_PMID(CL_DISK,2), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.read_bytes */ + { PMDA_PMID(CL_DISK,3), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.dev.write_bytes */ + { PMDA_PMID(CL_DISK,4), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.dev.total_bytes */ + { PMDA_PMID(CL_DISK,5), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.all.read */ + { PMDA_PMID(CL_DISK,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.write */ + { PMDA_PMID(CL_DISK,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.total */ + { PMDA_PMID(CL_DISK,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.read_bytes */ + { PMDA_PMID(CL_DISK,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.all.write_bytes */ + { PMDA_PMID(CL_DISK,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.all.total_bytes */ + { PMDA_PMID(CL_DISK,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.dev.blkread */ + { PMDA_PMID(CL_DISK,12), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.blkwrite */ + { PMDA_PMID(CL_DISK,13), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.blktotal */ + { PMDA_PMID(CL_DISK,14), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.blkread */ + { PMDA_PMID(CL_DISK,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.blkwrite */ + { PMDA_PMID(CL_DISK,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.blktotal */ + { PMDA_PMID(CL_DISK,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + + { NULL, /* network.interface.mtu */ + { PMDA_PMID(CL_NETIF,0), PM_TYPE_U32, NETIF_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { NULL, /* network.interface.up */ + { PMDA_PMID(CL_NETIF,1), PM_TYPE_U32, NETIF_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { NULL, /* network.interface.baudrate */ + { PMDA_PMID(CL_NETIF,2), PM_TYPE_U64, NETIF_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { NULL, /* network.interface.in.bytes */ + { PMDA_PMID(CL_NETIF,3), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* network.interface.in.packets */ + { PMDA_PMID(CL_NETIF,4), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.in.mcasts */ + { PMDA_PMID(CL_NETIF,5), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.in.errors */ + { PMDA_PMID(CL_NETIF,6), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.in.drops */ + { PMDA_PMID(CL_NETIF,7), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.bytes */ + { PMDA_PMID(CL_NETIF,8), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* network.interface.out.packets */ + { PMDA_PMID(CL_NETIF,9), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.mcasts */ + { PMDA_PMID(CL_NETIF,10), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.errors */ + { PMDA_PMID(CL_NETIF,11), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.collisions */ + { PMDA_PMID(CL_NETIF,12), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.total.bytes */ + { PMDA_PMID(CL_NETIF,13), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* network.interface.total.packets */ + { PMDA_PMID(CL_NETIF,14), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.total.mcasts */ + { PMDA_PMID(CL_NETIF,15), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.total.errors */ + { PMDA_PMID(CL_NETIF,16), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +}; +static int metrictablen = sizeof(metrictab) / sizeof(metrictab[0]); + +/* + * mapping between PCP metrics and sysctl metrics + * + * initialization note ... all elments after m_pcpname and m_name are OK + * to be 0 or NULL + */ +typedef struct { + const char *m_pcpname; /* PCP metric name or prefix (see matchname() */ + const char *m_name; /* sysctl metric name */ + size_t m_miblen; /* number of elements in m_mib[] */ + int *m_mib; + int m_fetched; /* 1 if m_data is current and valid */ + size_t m_datalen; /* number of bytes in m_data[] */ + void *m_data; /* value from sysctl */ +} mib_t; +static mib_t map[] = { + { "hinv.ncpu", "hw.ncpu" }, + { "hinv.physmem", "hw.physmem" }, + { "hinv.cpu.vendor", "hw.machine" }, + { "hinv.cpu.model", "hw.model" }, + { "hinv.cpu.arch", "hw.machine_arch" }, + { "kernel.all.load", "vm.loadavg" }, + { "kernel.all.hz", "kern.clockrate" }, + { "kernel.all.cpu.*", "kern.cp_time" }, + { "kernel.percpu.cpu.*", "kern.cp_times" }, + { "swap.pagesin", "vm.stats.vm.v_swappgsin" }, + { "swap.pagesout", "vm.stats.vm.v_swappgsout" }, + { "swap.in", "vm.stats.vm.v_swapin" }, + { "swap.out", "vm.stats.vm.v_swapout" }, + { "kernel.all.pswitch", "vm.stats.sys.v_swtch" }, + { "kernel.all.syscall", "vm.stats.sys.v_syscall" }, + { "kernel.all.intr", "vm.stats.sys.v_intr" }, + { "mem.util.bufmem", "vfs.bufspace" }, +/* + * DO NOT MOVE next 2 entries ... see note above for swap.free + */ + { "swap.length", "vm.swap_total" }, + { "swap.used", "vm.swap_reserved" }, +/* + * DO NOT MOVE next 6 entries ... see note above for mem.util.avail + * and mem.util.used + */ + { "mem.util.all", "vm.stats.vm.v_page_count" }, + { "mem.util.free", "vm.stats.vm.v_free_count" }, + { "mem.util.cached", "vm.stats.vm.v_cache_count" }, + { "mem.util.wired", "vm.stats.vm.v_wire_count" }, + { "mem.util.active", "vm.stats.vm.v_active_count" }, + { "mem.util.inactive", "vm.stats.vm.v_inactive_count" }, + +}; +static int maplen = sizeof(map) / sizeof(map[0]); +static mib_t bad_mib = { "bad.mib", "bad.mib", 0, NULL, 0, 0, NULL }; + +static char *username; +static int isDSO = 1; /* =0 I am a daemon */ +static int cpuhz; /* frequency for CPU time metrics */ +static int ncpu; /* number of cpus in kern.cp_times data */ +static int pagesize; /* vm page size */ + +/* + * Fetch values from sysctl() + * + * Expect the result to be xpect bytes to match the PCP data size or + * anticipated structure size, unless xpect is ==0 in which case the + * size test is skipped. + */ +static int +do_sysctl(mib_t *mp, size_t xpect) +{ + /* + * Note zero trip if mp->m_data and mp->datalen are already valid + * and current + */ + for ( ; mp->m_fetched == 0; ) { + int sts; + sts = sysctl(mp->m_mib, (u_int)mp->m_miblen, mp->m_data, &mp->m_datalen, NULL, 0); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "sysctl(%s%s) -> %d (datalen=%d)\n", mp->m_name, mp->m_data == NULL ? " firstcall" : "", sts, (int)mp->m_datalen); + } +#endif + if (sts == 0 && mp->m_data != NULL) { + mp->m_fetched = 1; + break; + } + if ((sts == -1 && errno == ENOMEM) || (sts == 0 && mp->m_data == NULL)) { + /* first call for this one, or data changed size */ + mp->m_data = realloc(mp->m_data, mp->m_datalen); + if (mp->m_data == NULL) { + fprintf(stderr, "Error: %s: buffer alloc failed for sysctl metric \"%s\"\n", mp->m_pcpname, mp->m_name); + __pmNoMem("do_sysctl", mp->m_datalen, PM_FATAL_ERR); + /*NOTREACHED*/ + } + } + else + return -errno; + } + if (xpect > 0 && mp->m_datalen != xpect) { + fprintf(stderr, "Error: %s: sysctl(%s) datalen=%d not %d!\n", mp->m_pcpname, mp->m_name, (int)mp->m_datalen, (int)xpect); + return 0; + } + return mp->m_datalen; +} + +/* + * kernel memory reader setup + */ +struct nlist symbols[] = { + { .n_name = "_ifnet" }, + { .n_name = NULL } +}; +kvm_t *kvmp; + +static void +kmemread_init(void) +{ + int sts; + int i; + char errmsg[_POSIX2_LINE_MAX]; + + /* + * If we're running as a daemon PMDA, assume we're setgid kmem, + * so we can open /dev/kmem, and downgrade privileges after the + * kvm_open(). + * For a DSO PMDA, we have to assume pmcd has the required + * privileges and don't dink with them. + */ + kvmp = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errmsg); + if (!isDSO) + setgid(getgid()); + if (kvmp == NULL) { + fprintf(stderr, "kmemread_init: kvm_openfiles failed: %s\n", errmsg); + return; + } + + sts = kvm_nlist(kvmp, symbols); + if (sts < 0) { + fprintf(stderr, "kmemread_init: kvm_nlist failed: %s\n", pmErrStr(-errno)); + for (i = 0; i < sizeof(symbols)/sizeof(symbols[0])-1; i++) + symbols[i].n_value = 0; + return; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + for (i = 0; i < sizeof(symbols)/sizeof(symbols[0])-1; i++) { + fprintf(stderr, "Info: kernel symbol %s found at 0x%08lx\n", symbols[i].n_name, symbols[i].n_value); + } + } +#endif + +} + +/* + * Callback provided to pmdaFetch ... come here once per metric-instance + * pair in each pmFetch(). + */ +static int +freebsd_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + int sts = PM_ERR_PMID; + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + mib_t *mp; + + mp = (mib_t *)mdesc->m_user; + if (idp->cluster == CL_SYSCTL) { + /* sysctl() simple cases */ + switch (idp->item) { + /* 32-bit integer values */ + case 0: /* hinv.ncpu */ + case 18: /* swap.pagesin */ + case 19: /* swap.pagesout */ + case 20: /* swap.in */ + case 21: /* swap.out */ + case 22: /* kernel.all.pswitch */ + case 23: /* kernel.all.syscall */ + case 24: /* kernel.all.intr */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data); + sts = 1; + } + break; + + /* 64-bit integer values */ + case 1: /* hinv.physmem */ + case 25: /* swap.length */ + case 26: /* swap.used */ + sts = do_sysctl(mp, sizeof(atom->ull)); + if (sts > 0) { + atom->ull = *((__uint64_t *)mp->m_data); + sts = 1; + } + break; + + /* string values */ + case 15: /* hinv.cpu.vendor */ + case 16: /* hinv.cpu.model */ + case 17: /* hinv.cpu.arch */ + sts = do_sysctl(mp, (size_t)0); + if (sts > 0) { + atom->cp = (char *)mp->m_data; + sts = 1; + } + break; + + /* structs and aggregates */ + case 2: /* kernel.all.load */ + sts = do_sysctl(mp, 3*sizeof(atom->d)); + if (sts > 0) { + int i; + struct loadavg *lp = (struct loadavg *)mp->m_data; + if (inst == 1) + i = 0; + else if (inst == 5) + i = 1; + else if (inst == 15) + i = 2; + else + return PM_ERR_INST; + atom->f = (float)((double)lp->ldavg[i] / lp->fscale); + sts = 1; + } + break; + + case 3: /* kernel.all.cpu.user */ + case 4: /* kernel.all.cpu.nice */ + case 5: /* kernel.all.cpu.sys */ + case 6: /* kernel.all.cpu.intr */ + case 7: /* kernel.all.cpu.idle */ + sts = do_sysctl(mp, CPUSTATES*sizeof(atom->ull)); + if (sts > 0) { + /* + * PMID assignment is important in the "-3" below so + * that metrics map to consecutive elements of the + * returned value in the order defined for CPUSTATES, + * i.e. CP_USER, CP_NICE, CP_SYS, CP_INTR and + * CP_IDLE + */ + atom->ull = 1000*((__uint64_t *)mp->m_data)[idp->item-3]/cpuhz; + sts = 1; + } + break; + + case 8: /* kernel.percpu.cpu.user */ + case 9: /* kernel.percpu.cpu.nice */ + case 10: /* kernel.percpu.cpu.sys */ + case 11: /* kernel.percpu.cpu.intr */ + case 12: /* kernel.percpu.cpu.idle */ + sts = do_sysctl(mp, ncpu*CPUSTATES*sizeof(atom->ull)); + if (sts > 0) { + /* + * PMID assignment is important in the "-8" below so + * that metrics map to consecutive elements of the + * returned value in the order defined for CPUSTATES, + * i.e. CP_USER, CP_NICE, CP_SYS, CP_INTR and + * CP_IDLE, and then there is one such set for each + * CPU up to the maximum number of CPUs installed in + * the system. + */ + atom->ull = 1000*((__uint64_t *)mp->m_data)[inst * CPUSTATES + idp->item-8]/cpuhz; + sts = 1; + } + break; + + case 13: /* kernel.all.hz */ + sts = do_sysctl(mp, sizeof(struct clockinfo)); + if (sts > 0) { + struct clockinfo *cp = (struct clockinfo *)mp->m_data; + atom->ul = cp->hz; + sts = 1; + } + break; + + } + } + else if (idp->cluster == CL_SPECIAL) { + /* special cases */ + switch (idp->item) { + case 0: /* hinv.ndisk */ + refresh_disk_metrics(); + atom->ul = pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_SIZE_ACTIVE); + sts = 1; + break; + + case 1: /* swap.free */ + /* first vm.swap_total */ + sts = do_sysctl(mp, sizeof(atom->ull)); + if (sts > 0) { + atom->ull = *((__uint64_t *)mp->m_data); + /* + * now subtract vm.swap_reserved ... assumes consecutive + * mib[] entries + */ + mp++; + sts = do_sysctl(mp, sizeof(atom->ull)); + if (sts > 0) { + atom->ull -= *((__uint64_t *)mp->m_data); + sts = 1; + } + } + break; + + case 3: /* hinv.pagesize */ + atom->ul = pagesize; + sts = 1; + break; + + case 4: /* mem.util.all */ + case 6: /* mem.util.free */ + case 8: /* mem.util.cached */ + case 9: /* mem.util.wired */ + case 10: /* mem.util.active */ + case 11: /* mem.util.inactive */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data) * (pagesize / 1024); + sts = 1; + } + break; + + case 7: /* mem.util.bufmem */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data) / 1024; + sts = 1; + } + break; + + case 5: /* mem.util.used */ + /* + * mp-> v_page_count entry in mib[] + * assuming consecutive mib[] entries, we want + * v_page_count mp[0] - v_free_count mp[1] - + * v_cache_count mp[2] - v_inactive_count mp[5] + */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data); + sts = do_sysctl(&mp[1], sizeof(atom->ul)); + if (sts > 0) { + atom->ul -= *((__uint32_t *)mp[1].m_data); + sts = do_sysctl(&mp[2], sizeof(atom->ul)); + if (sts > 0) { + atom->ul -= *((__uint32_t *)mp[2].m_data); + sts = do_sysctl(&mp[5], sizeof(atom->ul)); + if (sts > 0) { + atom->ul -= *((__uint32_t *)mp[5].m_data); + atom->ul *= (pagesize / 1024); + sts = 1; + } + } + } + } + break; + + case 12: /* mem.util.avail */ + /* + * mp-> v_page_count entry in mib[] + * assuming consecutive mib[] entries, we want + * v_free_count mp[1] + v_cache_count mp[2] + + * v_inactive_count mp[5] + */ + sts = do_sysctl(&mp[1], sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp[1].m_data); + sts = do_sysctl(&mp[2], sizeof(atom->ul)); + if (sts > 0) { + atom->ul += *((__uint32_t *)mp[2].m_data); + sts = do_sysctl(&mp[5], sizeof(atom->ul)); + if (sts > 0) { + atom->ul += *((__uint32_t *)mp[5].m_data); + atom->ul *= (pagesize / 1024); + sts = 1; + } + } + } + break; + + } + } + else if (idp->cluster == CL_DISK) { + /* disk metrics */ + sts = do_disk_metrics(mdesc, inst, atom); + } + else if (idp->cluster == CL_NETIF) { + /* network interface metrics */ + sts = do_netif_metrics(mdesc, inst, atom); + } + + return sts; +} + +/* + * wrapper for pmdaFetch ... force value caches to be reloaded if needed, + * then do the fetch + */ +static int +freebsd_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; + int done_disk = 0; + int done_netif = 0; + + for (i = 0; i < maplen; i++) { + map[i].m_fetched = 0; + } + + /* + * pre-fetch all metrics if needed, and update instance domains if + * they have changed + */ + for (i = 0; !done_disk && !done_netif && i < numpmid; i++) { + if (pmid_cluster(pmidlist[i]) == CL_DISK) { + refresh_disk_metrics(); + done_disk = 1; + } + else if (pmid_cluster(pmidlist[i]) == CL_NETIF) { + refresh_netif_metrics(); + done_netif = 1; + } + } + + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * wrapper for pmdaInstance ... refresh required instance domain first + */ +static int +freebsd_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + /* + * indomtab[] instance names and ids are not used for some indoms, + * ensure pmdaCache is current + */ + if (indom == indomtab[DISK_INDOM].it_indom) + refresh_disk_metrics(); + if (indom == indomtab[NETIF_INDOM].it_indom) + refresh_netif_metrics(); + + return pmdaInstance(indom, inst, name, result, pmda); +} + +/* + * PCP metric name matching for linking metrictab[] entries to mib[] + * entries. + * + * Return 1 if prefix[] is equal to, or a prefix of name[] + * + * prefix[] of the form "a.bc" or "a.bc*" matches a name[] like "a.bc" + * or "a.bcanything", to improve readability of the initializers in + * mib[], and asterisk is a "match all" special case, so "a.b.*" matches + * "a.b.anything" + */ +static int +matchname(const char *prefix, const char *name) +{ + while (*prefix != '\0' && *name != '\0' && *prefix == *name) { + prefix++; + name++; + } + if (*prefix == '\0' || *prefix == '*') + return 1; + else + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + * + * Do mapping from sysclt(3) names to mibs. + * Collect some global constants. + * Build the system-specific, but not dynamic, instance domains, + * e.g. CPU_INDOM. + * Initialize the kernel memory reader. + */ +void +freebsd_init(pmdaInterface *dp) +{ + int i; + int m; + int sts; + struct clockinfo clockrates; + size_t sz; + int mib[CTL_MAXNAME]; /* enough for longest mib key */ + char iname[16]; /* enough for cpuNN.. */ + + if (isDSO) { + char mypath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "freebsd" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_5, "freebsd DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.four.fetch = freebsd_fetch; + dp->version.four.instance = freebsd_instance; + + pmdaSetFetchCallBack(dp, freebsd_fetchCallBack); + + pmdaInit(dp, indomtab, indomtablen, metrictab, metrictablen); + + /* + * Link metrictab[] entries via m_user to map[] entries based on + * matching sysctl(3) name + * + * also translate the sysctl(3) name to a mib + */ + for (m = 0; m < metrictablen; m++) { + if (metrictab[m].m_user == NULL) { + /* not using sysctl(3) */ + continue; + } + for (i = 0; i < maplen; i++) { + if (matchname(map[i].m_pcpname, (char *)metrictab[m].m_user)) { + if (map[i].m_mib == NULL) { + /* + * multiple metrictab[] entries may point to the same + * mib[] entry, but this is the first time for this + * mib[] entry ... + */ + map[i].m_miblen = sizeof(mib); + sts = sysctlnametomib(map[i].m_name, mib, &map[i].m_miblen); + if (sts == 0) { + map[i].m_mib = (int *)malloc(map[i].m_miblen*sizeof(map[i].m_mib[0])); + if (map[i].m_mib == NULL) { + fprintf(stderr, "Error: %s (%s): failed mib alloc for sysctl metric \"%s\"\n", map[i].m_pcpname, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name); + __pmNoMem("freebsd_init: mib", map[i].m_miblen*sizeof(map[i].m_mib[0]), PM_FATAL_ERR); + /*NOTREACHED*/ + } + memcpy(map[i].m_mib, mib, map[i].m_miblen*sizeof(map[i].m_mib[0])); + } + else { + fprintf(stderr, "Error: %s (%s): failed sysctlnametomib(\"%s\", ...): %s\n", map[i].m_pcpname, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name, pmErrStr(-errno)); + metrictab[m].m_user = (void *)&bad_mib; + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + int p; + fprintf(stderr, "Info: %s (%s): sysctl metric \"%s\" -> ", (char *)metrictab[m].m_user, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name); + for (p = 0; p < map[i].m_miblen; p++) { + if (p > 0) fputc('.', stderr); + fprintf(stderr, "%d", map[i].m_mib[p]); + } + fputc('\n', stderr); + } +#endif + metrictab[m].m_user = (void *)&map[i]; + break; + } + } + if (i == maplen) { + fprintf(stderr, "Error: %s (%s): cannot match name in sysctl map[]\n", (char *)metrictab[m].m_user, pmIDStr(metrictab[m].m_desc.pmid)); + metrictab[m].m_user = (void *)&bad_mib; + } + } + + /* + * Collect some global constants needed later ... + */ + sz = sizeof(clockrates); + sts = sysctlbyname("kern.clockrate", &clockrates, &sz, NULL, 0); + if (sts < 0) { + fprintf(stderr, "Fatal Error: sysctlbyname(\"kern.clockrate\", ...) failed: %s\n", pmErrStr(-errno)); + exit(1); + } + cpuhz = clockrates.stathz; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Info: CPU time \"hz\" = %d\n", cpuhz); +#endif + + sts = sysctlbyname("kern.cp_times", NULL, &sz, NULL, 0); + if (sts < 0) { + fprintf(stderr, "Fatal Error: sysctlbyname(\"kern.cp_times\", ...) failed: %s\n", pmErrStr(-errno)); + exit(1); + } + /* + * see note below when fetching kernel.percpu.cpu.* metrics to + * explain this + */ + ncpu = sz / (CPUSTATES * sizeof(__uint64_t)); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Info: ncpu = %d\n", ncpu); +#endif + + sz = sizeof(pagesize); + sts = sysctlbyname("hw.pagesize", &pagesize, &sz, NULL, 0); + if (sts < 0) { + fprintf(stderr, "Fatal Error: sysctlbyname(\"hw.pagesize\", ...) failed: %s\n", pmErrStr(-errno)); + exit(1); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Info: VM pagesize = %d\n", pagesize); +#endif + + /* + * Build some instance domains ... + */ + indomtab[CPU_INDOM].it_numinst = ncpu; + indomtab[CPU_INDOM].it_set = (pmdaInstid *)malloc(ncpu * sizeof(pmdaInstid)); + if (indomtab[CPU_INDOM].it_set == NULL) { + __pmNoMem("freebsd_init: CPU_INDOM it_set", ncpu * sizeof(pmdaInstid), PM_FATAL_ERR); + /*NOTREACHED*/ + } + for (i = 0; i < ncpu; i++) { + indomtab[CPU_INDOM].it_set[i].i_inst = i; + snprintf(iname, sizeof(iname), "cpu%d", i); + indomtab[CPU_INDOM].it_set[i].i_name = strdup(iname); + if (indomtab[CPU_INDOM].it_set[i].i_name == NULL) { + __pmNoMem("freebsd_init: CPU_INDOM strdup iname", strlen(iname), PM_FATAL_ERR); + /*NOTREACHED*/ + } + } + + kmemread_init(); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n\n", pmProgname); + fputs("Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -U username user account to run under (default \"pcp\")\n" + "\nExactly one of the following options may appear:\n" + " -i port expect PMCD to connect on given inet port (number or name)\n" + " -p expect PMCD to supply stdin/stdout (pipe)\n" + " -u socket expect PMCD to connect on given unix domain socket\n" + " -6 port expect PMCD to connect on given ipv6 port (number or name)\n", + stderr); + exit(1); +} + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int c, err = 0; + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char mypath[MAXPATHLEN]; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "freebsd" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_5, pmProgname, FREEBSD, + "freebsd.log", mypath); + + while ((c = pmdaGetOpt(argc, argv, "D:d:i:l:pu:U:6:?", &dispatch, &err)) != EOF) { + switch(c) { + case 'U': + username = optarg; + break; + default: + err++; + } + } + if (err) + usage(); + + pmdaOpenLog(&dispatch); + freebsd_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/freebsd/freebsd.h b/src/pmdas/freebsd/freebsd.h new file mode 100644 index 0000000..c6b9b72 --- /dev/null +++ b/src/pmdas/freebsd/freebsd.h @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2012 Ken McDonell All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * instance domains control + */ +#define LOADAV_INDOM 0 +#define CPU_INDOM 1 +#define DISK_INDOM 2 +#define NETIF_INDOM 3 +extern pmdaIndom indomtab[]; + +extern void refresh_disk_metrics(void); +extern int do_disk_metrics(pmdaMetric *, unsigned int, pmAtomValue *); + +extern void refresh_netif_metrics(void); +extern int do_netif_metrics(pmdaMetric *, unsigned int, pmAtomValue *); + +/* + * kernel memory reader pieces + */ +#include +#include + +extern kvm_t *kvmp; + +#define KERN_IFNET 0 +extern struct nlist symbols[]; diff --git a/src/pmdas/freebsd/help b/src/pmdas/freebsd/help new file mode 100644 index 0000000..5bb566a --- /dev/null +++ b/src/pmdas/freebsd/help @@ -0,0 +1,95 @@ +@ FREEBSD.0 Instance domain for load average +Universally 3 instances, "1 minute" (1), "5 minute" (5) and +"15 minute" (15). + +@ FREEBSD.1 CPU Instance domain for kernel.percpu metrics +One instance for each physical CPU. + +@ FREEBSD.2 DISK Instance domain for disk.dev metrics +One instance for each physical "direct access" device. + +@ kernel.all.hz system hz rate +Microseconds per "hz" tick. +@ kernel.all.load 1, 5 and 15 minute load average +@ kernel.all.pswitch count of context switches +@ kernel.all.syscall count of system calls +@ kernel.all.intr count of interrupts serviced +@ kernel.all.cpu.user total user CPU time for all CPUs +@ kernel.all.cpu.nice total nice user CPU time for all CPUs +@ kernel.all.cpu.sys total sys CPU time for all CPUs +@ kernel.all.cpu.intr total interrupt CPU time for all CPUs +@ kernel.all.cpu.idle total idle CPU time for all CPUs +@ kernel.percpu.cpu.user user CPU time for each CPU +@ kernel.percpu.cpu.nice nice user CPU time for each CPU +@ kernel.percpu.cpu.sys sys CPU time for each CPU +@ kernel.percpu.cpu.intr interrupt CPU time for each CPU +@ kernel.percpu.cpu.idle idle CPU time for each CPU + +@ hinv.ncpu number of CPUs in the system +@ hinv.ndisk number of disks in the system +@ hinv.physmem total system memory +@ hinv.pagesize kernel page size +@ hinv.cpu.vendor system's CPU vendor +@ hinv.cpu.model system's CPU model +@ hinv.cpu.arch system's machine dependent CPU architecture type + +@ swap.length total swap space size +@ swap.used reserved (or allocated) swap space +@ swap.free available swap space + +@ swap.pagesin pages read from external storage to service page faults +@ swap.pagesout dirty pages written to swap devices +When the rate of page writes is non-zero, this is the most useful +indication severe demand for physical memory. +@ swap.in number of swap in operations +@ swap.out number of swap out operations + +@ disk.dev.read Count of read operations per disk +@ disk.dev.write Count of write operations per disk +@ disk.dev.total Count of read or write operations (IOPs) per disk +@ disk.dev.read_bytes Count of bytes read from each disk +@ disk.dev.write_bytes Count of bytes written to each disk +@ disk.dev.total_bytes Count of bytes transferred to or from each disk +@ disk.dev.blkread Count of blocks read from each disk +@ disk.dev.blkwrite Count of blocks written to each disk +@ disk.dev.blktotal Count of blocks transferred to or from each disk +@ disk.all.read Count of read operations across all disks +@ disk.all.write Count of write operations across all disks +@ disk.all.total Count of read or write operations (IOPs) across all disks +@ disk.all.read_bytes Count of bytes read from all disks +@ disk.all.write_bytes Count of bytes written to all disks +@ disk.all.total_bytes Count of bytes transferred to or from all disks +@ disk.all.blkread Count of blocks read from all disks +@ disk.all.blkwrite Count of blocks written to all disks +@ disk.all.blktotal Count of blocks transferred to or from all disks + +@ mem.util.all Total memory managed by the system +@ mem.util.used Memory that is actively in use +Equals "all" minus "free" minus "inactive" minus "cached". +@ mem.util.free Unallocated and free memory +@ mem.util.bufmem Memory associated with active buffer I/O +@ mem.util.cached Cached memory +Unmodified (clean and cached) pages from files in filesystems. +@ mem.util.wired Wired or pinned memory that cannot be paged out +@ mem.util.active Recently accessed memory +@ mem.util.inactive Memory that is in use, but has not be accessed recently +@ mem.util.avail Available memory +Free plus inactive plus cached. + +@ network.interface.mtu Maximum Transfer Unit for each network interface +@ network.interface.up "UP" state for each network interface +@ network.interface.baudrate Data baudrate for each network interface +@ network.interface.in.bytes Bytes received on each network interface +@ network.interface.in.packets Packets received on each network interface +@ network.interface.in.mcasts Multicast packets received on each network interface +@ network.interface.in.errors Input errors on each network interface +@ network.interface.in.drops Dropped packets on each network interface +@ network.interface.out.bytes Bytes transmitted on each network interface +@ network.interface.out.packets Packets transmitted on each network interface +@ network.interface.out.mcasts Multicast packets transmitted on each network interface +@ network.interface.out.errors Output errors on each network interface +@ network.interface.out.collisions Output collisions on each network interface +@ network.interface.total.bytes Bytes received or transmitted on each network interface +@ network.interface.total.packets Packets received or transmitted on each network interface +@ network.interface.total.mcasts Multicast packets received or transmitted on each network interface +@ network.interface.total.errors Input or output errors on each network interface diff --git a/src/pmdas/freebsd/netif.c b/src/pmdas/freebsd/netif.c new file mode 100644 index 0000000..c19d7f3 --- /dev/null +++ b/src/pmdas/freebsd/netif.c @@ -0,0 +1,225 @@ +/* + * FreeBSD Kernel PMDA - network interface metrics + * + * Copyright (c) 2012 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include +#include +#include +#include +#include + +#include "freebsd.h" + +#define WARN_INIT 1 +#define WARN_READ_HEAD 2 + +void +refresh_netif_metrics(void) +{ + int i; + int sts; + unsigned long kaddr; + struct ifnethead ifnethead; + struct ifnet ifnet; + struct ifnet *ifp; + static int warn = 0; /* warn once control */ + + /* + * Not sure that the order of chained netif structs is invariant, + * especially if interfaces are added to the configuration after + * initial system boot ... so mark all the instances as inactive + * and re-match based on the interface name + */ + pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_INACTIVE); + + kaddr = symbols[KERN_IFNET].n_value; + if (kvmp == NULL || kaddr == 0) { + /* no network interface metrics for us today ... */ + if ((warn & WARN_INIT) == 0) { + fprintf(stderr, "refresh_netif_metrics: Warning: cannot get any network interface metrics\n"); + warn |= WARN_INIT; + } + return; + } + + /* + * Kernel data structures for the linked list of network interface + * information. + * + * _ifnet -> struct ifnethead { + * struct ifnet *tqh_first; + * struct ifnet **tqh_last; + * ... + * } + * + * and within an ifnet struct (declared in ) we find + * the linked list maintained in if_link, the external interface + * name in if_xname[] and if_data which is a nested if_data stuct + * (declared in ) that contains many of the goodies we're + * after, e.g. u_char ifi_type, u_long ifi_mtu, u_long ifi_baudrate, + * u_long ifi_ipackets, u_long ifi_opackets, u_long ifi_ibytes, + * u_long ifi_obytes, etc. + */ + if (kvm_read(kvmp, kaddr, (char *)&ifnethead, sizeof(ifnethead)) != sizeof(ifnethead)) { + if ((warn & WARN_READ_HEAD) == 0) { + fprintf(stderr, "refresh_netif_metrics: Warning: kvm_read: ifnethead: %s\n", kvm_geterr(kvmp)); + warn |= WARN_READ_HEAD; + } + return; + } + + for (i = 0; ; i++) { + if (i == 0) + kaddr = (unsigned long)TAILQ_FIRST(&ifnethead); + else + kaddr = (unsigned long)TAILQ_NEXT(&ifnet, if_link); + + if (kaddr == 0) + break; + + if (kvm_read(kvmp, kaddr, (char *)&ifnet, sizeof(ifnet)) != sizeof(ifnet)) { + fprintf(stderr, "refresh_netif_metrics: Error: kvm_read: ifnet[%d]: %s\n", i, kvm_geterr(kvmp)); + return; + } + + /* skip network interfaces that are not interesting ... */ + if (strcmp(ifnet.if_xname, "lo0") == 0) + continue; + + sts = pmdaCacheLookupName(indomtab[NETIF_INDOM].it_indom, ifnet.if_xname, NULL, (void **)&ifp); + if (sts == PMDA_CACHE_ACTIVE) { + fprintf(stderr, "refresh_netif_metrics: Warning: duplicate name (%s) in network interface indom\n", ifnet.if_xname); + continue; + } + else if (sts == PMDA_CACHE_INACTIVE) { + /* reactivate an existing entry */ + pmdaCacheStore(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_ADD, ifnet.if_xname, (void *)ifp); + } + else { + /* new entry */ + ifp = (struct ifnet *)malloc(sizeof(*ifp)); + if (ifp == NULL) { + fprintf(stderr, "Error: struct ifnet alloc failed for network interface \"%s\"\n", ifnet.if_xname); + __pmNoMem("refresh_netif_metrics", sizeof(*ifp), PM_FATAL_ERR); + /*NOTREACHED*/ + } + pmdaCacheStore(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_ADD, ifnet.if_xname, (void *)ifp); + } + memcpy((void *)ifp, (void *)&ifnet, sizeof(*ifp)); + } +} + +int +do_netif_metrics(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + struct ifnet *ifp; + int sts = 0; + + if (inst != PM_IN_NULL) { + /* + * per-network interface metrics + */ + sts = pmdaCacheLookup(indomtab[NETIF_INDOM].it_indom, inst, NULL, (void **)&ifp); + if (sts == PMDA_CACHE_ACTIVE) { + sts = 1; + /* cluster and domain already checked, just need item ... */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 0: /* network.interface.mtu */ + atom->ul = (__uint32_t)ifp->if_data.ifi_mtu; + break; + + case 1: /* network.interface.up */ + atom->ul = (ifp->if_flags & IFF_UP) == IFF_UP; + break; + + case 2: /* network.interface.baudrate */ + atom->ull = ifp->if_data.ifi_baudrate; + break; + + case 3: /* network.interface.in.bytes */ + atom->ull = ifp->if_data.ifi_ibytes; + break; + + case 4: /* network.interface.in.packets */ + atom->ull = ifp->if_data.ifi_ipackets; + break; + + case 5: /* network.interface.in.mcasts */ + atom->ull = ifp->if_data.ifi_imcasts; + break; + + case 6: /* network.interface.in.errors */ + atom->ull = ifp->if_data.ifi_ierrors; + break; + + case 7: /* network.interface.in.drops */ + atom->ull = ifp->if_data.ifi_iqdrops; + break; + + case 8: /* network.interface.out.bytes */ + atom->ull = ifp->if_data.ifi_obytes; + break; + + case 9: /* network.interface.out.packets */ + atom->ull = ifp->if_data.ifi_opackets; + break; + + case 10: /* network.interface.out.mcasts */ + atom->ull = ifp->if_data.ifi_omcasts; + break; + + case 11: /* network.interface.out.errors */ + atom->ull = ifp->if_data.ifi_oerrors; + break; + + case 12: /* network.interface.out.collisions */ + atom->ull = ifp->if_data.ifi_collisions; + break; + + case 13: /* network.interface.total.bytes */ + atom->ull = ifp->if_data.ifi_ibytes + ifp->if_data.ifi_obytes; + break; + + case 14: /* network.interface.total.packets */ + atom->ull = ifp->if_data.ifi_ipackets + ifp->if_data.ifi_opackets; + break; + + case 15: /* network.interface.total.mcasts */ + atom->ull = ifp->if_data.ifi_imcasts + ifp->if_data.ifi_omcasts; + break; + + case 16: /* network.interface.total.errors */ + atom->ull = ifp->if_data.ifi_ierrors + ifp->if_data.ifi_oerrors; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else + sts = 0; + } + + return sts; +} diff --git a/src/pmdas/freebsd/root_freebsd b/src/pmdas/freebsd/root_freebsd new file mode 100644 index 0000000..6f56d7e --- /dev/null +++ b/src/pmdas/freebsd/root_freebsd @@ -0,0 +1,172 @@ +/* + * Metrics for FreeBSD kernel PMDA + * + * Copyright (c) 2012 Ken McDonell All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * the domain for the FreeBSD PMDA ... + */ +#ifndef FREEBSD +#define FREEBSD 85 +#endif + +root { + hinv + kernel + disk + mem + network + swap +} + +hinv { + ncpu FREEBSD:0:0 + ndisk FREEBSD:1:0 + physmem FREEBSD:0:1 + pagesize FREEBSD:1:3 + cpu +} + +hinv.cpu { + vendor FREEBSD:0:15 + model FREEBSD:0:16 + arch FREEBSD:0:17 +} + +kernel { + all + percpu +} + +kernel.all { + pswitch FREEBSD:0:22 + syscall FREEBSD:0:23 + intr FREEBSD:0:24 + hz FREEBSD:0:13 + load FREEBSD:0:2 + cpu +} + +kernel.all.cpu { + user FREEBSD:0:3 + nice FREEBSD:0:4 + sys FREEBSD:0:5 + intr FREEBSD:0:6 + idle FREEBSD:0:7 +} + +kernel.percpu { + cpu +} + +kernel.percpu.cpu { + user FREEBSD:0:8 + nice FREEBSD:0:9 + sys FREEBSD:0:10 + intr FREEBSD:0:11 + idle FREEBSD:0:12 +} + +disk { + dev + all +} + +disk.dev { + read FREEBSD:2:0 + write FREEBSD:2:1 + total FREEBSD:2:2 + read_bytes FREEBSD:2:3 + write_bytes FREEBSD:2:4 + total_bytes FREEBSD:2:5 + blkread FREEBSD:2:12 + blkwrite FREEBSD:2:13 + blktotal FREEBSD:2:14 +} + +disk.all { + read FREEBSD:2:6 + write FREEBSD:2:7 + total FREEBSD:2:8 + read_bytes FREEBSD:2:9 + write_bytes FREEBSD:2:10 + total_bytes FREEBSD:2:11 + blkread FREEBSD:2:15 + blkwrite FREEBSD:2:16 + blktotal FREEBSD:2:17 +} + +mem { + util +} + +mem.util { + all FREEBSD:1:4 + used FREEBSD:1:5 + free FREEBSD:1:6 + bufmem FREEBSD:1:7 + cached FREEBSD:1:8 + wired FREEBSD:1:9 + active FREEBSD:1:10 + inactive FREEBSD:1:11 + avail FREEBSD:1:12 +} + +network { + interface +} + +network.interface { + mtu FREEBSD:3:0 + up FREEBSD:3:1 + baudrate FREEBSD:3:2 + in + out + total +} + +network.interface.in { + bytes FREEBSD:3:3 + packets FREEBSD:3:4 + mcasts FREEBSD:3:5 + errors FREEBSD:3:6 + drops FREEBSD:3:7 +} + +network.interface.out { + bytes FREEBSD:3:8 + packets FREEBSD:3:9 + mcasts FREEBSD:3:10 + errors FREEBSD:3:11 + collisions FREEBSD:3:12 +} + +network.interface.total { + bytes FREEBSD:3:13 + packets FREEBSD:3:14 + mcasts FREEBSD:3:15 + errors FREEBSD:3:16 +} + +swap { + pagesin FREEBSD:0:18 + pagesout FREEBSD:0:19 + in FREEBSD:0:20 + out FREEBSD:0:21 + length FREEBSD:0:25 + used FREEBSD:0:26 + free FREEBSD:1:1 +} + +#undef FREEBSD diff --git a/src/pmdas/gfs2/GNUmakefile b/src/pmdas/gfs2/GNUmakefile new file mode 100644 index 0000000..03fce96 --- /dev/null +++ b/src/pmdas/gfs2/GNUmakefile @@ -0,0 +1,60 @@ +# +# Copyright (c) 2013 - 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +CFILES = latency.c control.c glstats.c worst_glock.c glocks.c sbstats.c ftrace.c pmda.c +HFILES = latency.c control.h glstats.h worst_glock.h glocks.h sbstats.h ftrace.h pmdagfs2.h +CMDTARGET = pmdagfs2 +LLDLIBS = $(PCP_PMDALIB) +LSRCFILES = Install Remove pmns root help README + +IAM = gfs2 +DOMAIN = GFS2 +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(CMDTARGET) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root pmns domain.h help $(PMDADIR) +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +pmda.o glocks.o: glocks.h +pmda.o sbstats.o: sbstats.h +pmda.o: pmdagfs2.h +pmda.o glstats.o: glstats.h +pmda.o ftrace.o: ftrace.h +pmda.o ftrace.o worst_glock.o: worst_glock.h +pmda.o ftrace.o latency.o: latency.h +pmda.o control.o: control.h diff --git a/src/pmdas/gfs2/Install b/src/pmdas/gfs2/Install new file mode 100644 index 0000000..5f60ce2 --- /dev/null +++ b/src/pmdas/gfs2/Install @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the GFS2 PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gfs2 +forced_restart=true +pmda_interface=4 + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/gfs2/README b/src/pmdas/gfs2/README new file mode 100644 index 0000000..13e5228 --- /dev/null +++ b/src/pmdas/gfs2/README @@ -0,0 +1,108 @@ +Performance Co-Pilot PMDA for Monitoring GFS2 Filesystems +========================================================= + +This PMDA is capable of collecting glock statistics from GFS2 filesystems +mounted on the system in both local and clustered configurations. + +The PMDA collects its data from the trace-point output given by GFS2 as +the filesystem is working, this information is provided in userland +through debugfs. In order for pmdagfs2 to be able to provide any metric +data the user must have debugfs mounted and at least on GFS2 filesystem +mounted on the system to be monitored. + +As mentioned above the PMDA can be used both situations where GFS2 +filesystems are mounted as local filesystems and in cluster configurations +where GFS2 is used as a shared disk filesystem. When the PMDA is being +used in conjunction with a locally mounted filesystem (no clustering) only +a base number of metrics will be available to provide metric information +back to PMCD, these metrics can be recognised by their lack of +corresponding “control” metrics. + +For configurations where GFS2 is used in a clustered environment the +additional “cluster only” metrics are able to collect data through the +cache control mechanism of the cluster. This data being passed between +cluster nodes regarding the state of glocks is unavailable in local +filesystem configurations leading the requirement for a cluster +configuration for these metrics. + +For more information on GFS2 or cluster setups please visit www.redhat.com + +Metrics +======= + +The file ./help contains descriptions for all of the metrics which are +exposed by this PMDA. + +Once the PMDA has been installed, the following command will list all of +the available metrics and their explanatory “help” text: + + + # $ pminfo -fT gfs2 + +Installation +============ + + + # cd $PCP_PMDAS_DIR/gfs2 + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDA's currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number (This should only be an issue on installations with + third party PMDA's installed as the domain number given has been + reserved for the GFS2 PMDA with base PCP installations). + + + Then simply use + + # ./Install + + and choose both the “collector” and “monitor” installation + configuration options. + +Configuration +============= + +Some of the metrics provided by the PMDA can be configured to whether they +are turned on or off with regards to collecting metric data. These metrics +are distinguished by having a corresponding “control” metric. + +Identification of these metrics which have this control can be found by +issuing the following command. + + + $ pminfo -fT gfs2.control + +The metrics given as output through pminfo in this way can be controlled +by setting their metric value to either 0 (Off: no collection of metric +data) or 1 (On: collection of metric data) using the provided command +pmstore whilst specifying the metric to set the value for and a valid +value. + + + $ pmstore gfs2.control.tracepoints.all 1 + +Any metrics without a corresponding control metric cannot have their +functionality toggled in this way. + +De-Installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/gfs2 + #./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/PMCD/gfs2.log) should be checked for any warnings or + errors. + + + In an event where no values are being returned for most of the + metrics check ensure that both debugfs is mounted, metrics with + control options are enabled and your distribution supports the + full range of GFS2 trace-points. + + $ mount -t debugfs none /sys/kernel/debug + + $ pminfo -f gfs2.control + + $ pmstore gfs2.control.tracepoints.all 1 diff --git a/src/pmdas/gfs2/Remove b/src/pmdas/gfs2/Remove new file mode 100644 index 0000000..ba5409c --- /dev/null +++ b/src/pmdas/gfs2/Remove @@ -0,0 +1,24 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the GFS2 PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gfs2 +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/gfs2/control.c b/src/pmdas/gfs2/control.c new file mode 100644 index 0000000..20d5c6f --- /dev/null +++ b/src/pmdas/gfs2/control.c @@ -0,0 +1,123 @@ +/* + * GFS2 trace-point metrics control. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "control.h" +#include "ftrace.h" +#include "worst_glock.h" +#include "latency.h" + +/* Locations of the enable files for the gfs2 tracepoints */ +const char *control_locations[] = { + [CONTROL_ALL] = "/sys/kernel/debug/tracing/events/gfs2/enable", + [CONTROL_GLOCK_STATE_CHANGE] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_state_change/enable", + [CONTROL_GLOCK_PUT] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_put/enable", + [CONTROL_DEMOTE_RQ] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_demote_rq/enable", + [CONTROL_PROMOTE] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_promote/enable", + [CONTROL_GLOCK_QUEUE] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_queue/enable", + [CONTROL_GLOCK_LOCK_TIME] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_lock_time/enable", + [CONTROL_PIN] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_pin/enable", + [CONTROL_LOG_FLUSH] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_log_flush/enable", + [CONTROL_LOG_BLOCKS] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_log_blocks/enable", + [CONTROL_AIL_FLUSH] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_ail_flush/enable", + [CONTROL_BLOCK_ALLOC] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_block_alloc/enable", + [CONTROL_BMAP] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_bmap/enable", + [CONTROL_RS] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_rs/enable", + [CONTROL_BUFFER_SIZE_KB] = "/sys/kernel/debug/tracing/buffer_size_kb", + [CONTROL_GLOBAL_TRACING] = "/sys/kernel/debug/tracing/tracing_on" +}; + +/* + * Refresh callback for the control metrics; For traceppoints that have file + * based enabling we use gfs2_control_check_value(), for other metrics we + * call their corresponding "get" value. + */ +int +gfs2_control_fetch(int item, pmAtomValue *atom) +{ + if (item >= CONTROL_ALL && item <= CONTROL_GLOBAL_TRACING) { + atom->ul = gfs2_control_check_value(control_locations[item]); + + } else if (item == CONTROL_WORSTGLOCK) { + atom->ul = worst_glock_get_state(); + + } else if (item == CONTROL_LATENCY) { + atom->ul = latency_get_state(); + + } else if (item == CONTROL_FTRACE_GLOCK_THRESHOLD) { + atom->ul = ftrace_get_threshold(); + + } else { + return PM_ERR_PMID; + + } + return 1; +} + +/* + * Attempt open the enable file for the given filename and set the value + * contained in pmValueSet. The enable file for the tracepoint only accept + * 0 for disabled or 1 for enabled. + */ +int +gfs2_control_set_value(const char *filename, pmValueSet *vsp) +{ + FILE *fp; + int sts = 0; + int value = vsp->vlist[0].value.lval; + + if (strncmp(filename, control_locations[CONTROL_BUFFER_SIZE_KB], + sizeof(control_locations[CONTROL_BUFFER_SIZE_KB])-1) == 0) { + /* Special case for buffer_size_kb */ + if (value < 0 || value > 131072) /* Allow upto 128mb buffer per CPU */ + return - oserror(); + + } else if (value < 0 || value > 1) { + return -oserror(); + } + + fp = fopen(filename, "w"); + if (!fp) { + sts = -oserror(); /* EACCESS, File not found (stats not supported) */; + } else { + fprintf(fp, "%d\n", value); + fclose(fp); + } + return sts; +} + +/* + * We check the tracepoint enable file given by filename and return the value + * contained. This should either be 0 for disabled or 1 for enabled. In the + * event of permissions or file not found we will return zero. + */ +int +gfs2_control_check_value(const char *filename) +{ + FILE *fp; + char buffer[16]; + int value = 0; + + fp = fopen(filename, "r"); + if (fp) { + while (fgets(buffer, sizeof(buffer), fp) != NULL) + sscanf(buffer, "%d", &value); + fclose(fp); + } + return value; +} diff --git a/src/pmdas/gfs2/control.h b/src/pmdas/gfs2/control.h new file mode 100644 index 0000000..e687dad --- /dev/null +++ b/src/pmdas/gfs2/control.h @@ -0,0 +1,49 @@ +/* + * GFS2 trace-point enable controls. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CONTROL_H +#define CONTROL_H + +enum { + CONTROL_ALL = 0, + CONTROL_GLOCK_STATE_CHANGE, + CONTROL_GLOCK_PUT, + CONTROL_DEMOTE_RQ, + CONTROL_PROMOTE, + CONTROL_GLOCK_QUEUE, + CONTROL_GLOCK_LOCK_TIME, + CONTROL_PIN, + CONTROL_LOG_FLUSH, + CONTROL_LOG_BLOCKS, + CONTROL_AIL_FLUSH, + CONTROL_BLOCK_ALLOC, + CONTROL_BMAP, + CONTROL_RS, + CONTROL_BUFFER_SIZE_KB, + CONTROL_GLOBAL_TRACING, + CONTROL_WORSTGLOCK, + CONTROL_LATENCY, + CONTROL_FTRACE_GLOCK_THRESHOLD, + NUM_CONTROL_STATS +}; + +extern const char *control_locations[]; + +extern int gfs2_control_fetch(int, pmAtomValue *); +extern int gfs2_control_set_value(const char *, pmValueSet *); +extern int gfs2_control_check_value(const char *); + +#endif /* CONTROL_H */ diff --git a/src/pmdas/gfs2/ftrace.c b/src/pmdas/gfs2/ftrace.c new file mode 100644 index 0000000..6485c00 --- /dev/null +++ b/src/pmdas/gfs2/ftrace.c @@ -0,0 +1,586 @@ +/* + * GFS2 ftrace based trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "pmdagfs2.h" +#include "ftrace.h" +#include "worst_glock.h" +#include "latency.h" + +#include +#include +#include +#include +#include + + +static char *TRACE_PIPE = "/sys/kernel/debug/tracing/trace_pipe"; +static int max_glock_throughput = INITIAL_GLOBAL_MAX_GLOCK_THROUGHPUT; + +static struct ftrace_data ftrace_data; +static int num_accepted_entries; + +/* + * Fetches the value for the given metric item and then assigns to pmAtomValue. + * We check to see if item is in valid range for the metric. + */ +int +gfs2_ftrace_fetch(int item, struct ftrace *ftrace, pmAtomValue *atom) +{ + /* Ensure that metric value wanted is valid */ + if ((item < 0 || item >= NUM_TRACEPOINT_STATS)) + return PM_ERR_PMID; + + atom->ull = ftrace->values[item]; + return 1; +} + +/* + * External function to allow the increment of the num_accepted_locks + * variable from pmStore. + */ +void +ftrace_increase_num_accepted_entries(){ + num_accepted_entries++; +} + +/* + * Sets the value of max_glock_throughput using pmstore, value should + * must be positive. + */ +int +ftrace_set_threshold(pmValueSet *vsp) +{ + int value = vsp->vlist[0].value.lval; + + if (value < 0) /* Ensure positive value */ + return PM_ERR_SIGN; + + max_glock_throughput = value; + + return 0; +} + +/* + * Returns the max number of glocks we allow per run through the trace_pipe, + * Used by the fetch for the control metrics. + */ +int +ftrace_get_threshold() +{ + return max_glock_throughput; +} + +/* + * We open the ftrace trace file in write mode and straight after + * close it. This acts as a way to completely clear the trace ring- + * buffer when needed. + */ +static int +ftrace_clear_buffer() +{ + char *TRACE = "/sys/kernel/debug/tracing/trace"; + FILE *fp; + + /* Open in write mode and then straight close will purge buffer contents */ + if (( fp = fopen(TRACE, "w")) == NULL ) + return -oserror(); + + fclose(fp); + + return 0; +} + +/* + * We take tracedata from the trace pipe and store only the data which is + * from GFS2 metrics. We collect all the data in one array to be worked + * through later, this is because all trace data comes through the + * trace pipe mixed. + */ +static int +gfs2_extract_trace_values(char *buffer, pmInDom gfs_fs_indom) +{ + struct ftrace_data temp; + + unsigned int major, minor; + char *data; + + /* Interpret data, we work out what tracepoint it belongs to first */ + if ((data = strstr(buffer, "gfs2_glock_state_change: "))) { + temp.tracepoint = GLOCK_STATE_CHANGE; + sscanf(data, "gfs2_glock_state_change: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for latency metrics for processing, + * only if the metrics are enabled. + */ + if (latency_get_state() == 1) + gfs2_extract_latency(major, minor, temp.tracepoint, buffer, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_glock_put: "))) { + temp.tracepoint = GLOCK_PUT; + sscanf(data, "gfs2_glock_put: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_demote_rq: "))) { + temp.tracepoint = DEMOTE_RQ; + sscanf(data, "gfs2_demote_rq: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for latency metrics for processing, + * only if the metrics are enabled. + */ + if (latency_get_state() == 1) + gfs2_extract_latency(major, minor, temp.tracepoint, buffer, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_promote: "))) { + temp.tracepoint = PROMOTE; + sscanf(data, "gfs2_promote: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_glock_queue: "))) { + temp.tracepoint = GLOCK_QUEUE; + sscanf(data, "gfs2_glock_queue: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for latency metrics for processing, + * only if the metrics are enabled. + */ + if (latency_get_state() == 1) + gfs2_extract_latency(major, minor, temp.tracepoint, buffer, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_glock_lock_time: "))) { + temp.tracepoint = GLOCK_LOCK_TIME; + sscanf(data, "gfs2_glock_lock_time: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for worst_glock metrics for processing, + * only if the metrics are enabled. + */ + if (worst_glock_get_state() == 1) + gfs2_extract_worst_glock(&data, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_pin: "))) { + temp.tracepoint = PIN; + sscanf(data, "gfs2_pin: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_log_flush: "))) { + temp.tracepoint = LOG_FLUSH; + sscanf(data, "gfs2_log_flush: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_log_blocks: "))) { + temp.tracepoint = LOG_BLOCKS; + sscanf(data, "gfs2_log_blocks: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_ail_flush: "))) { + temp.tracepoint = AIL_FLUSH; + sscanf(data, "gfs2_ail_flush: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_block_alloc: "))) { + temp.tracepoint = BLOCK_ALLOC; + sscanf(data, "gfs2_block_alloc: %"SCNu32",%"SCNu32" %s", &major, &minor, data); + + } else if ((data = strstr(buffer, "gfs2_bmap: "))) { + temp.tracepoint = BMAP; + sscanf(data, "gfs2_bmap: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_rs: "))) { + temp.tracepoint = RS; + sscanf(data, "gfs2_rs: %"SCNu32",%"SCNu32" %s", &major, &minor, data); + } else { + return 0; /* If we do not have matching data, return and continue */ + } + + temp.dev_id = makedev(major, minor); + strncpy(temp.data, data, sizeof(temp.data)-1); + + /* Assign data in the array and update counters */ + ftrace_data = temp; + num_accepted_entries++; + + return 0; +} + +/* + * We work though each mounted filesystem and update the metric data based + * off what tracepoint information we have collected from the trace pipe. + */ +static void +gfs2_assign_ftrace(pmInDom gfs2_fs_indom, int reset_flag) +{ + int i, j, sts; + struct gfs2_fs *fs; + + /* We walk through for each filesystem */ + for (pmdaCacheOp(gfs2_fs_indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(gfs2_fs_indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + sts = pmdaCacheLookup(gfs2_fs_indom, i, NULL, (void **)&fs); + if (sts != PMDA_CACHE_ACTIVE) + continue; + + if(reset_flag == 1){ + for (j = 0; j < NUM_TRACEPOINT_STATS; j++) { + /* Reset old metric data for all tracepoints */ + fs->ftrace.values[j] = 0; + reset_flag = 0; + } + } + + if (fs->dev_id == ftrace_data.dev_id) { + + /* Work through the data, increasing metric counters */ + if (ftrace_data.tracepoint == GLOCK_STATE_CHANGE) { + char state[3], target[3]; + + sscanf(ftrace_data.data, + "gfs2_glock_state_change: %*d,%*d glock %*d:%*d state %*s to %s tgt:%s dmt:%*s flags:%*s", + state, target + ); + + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKSTATE_TOTAL]++; + + if (strncmp(state, target, 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_GLOCK_CHANGEDTARGET]++; + } else { + fs->ftrace.values[FTRACE_GLOCKSTATE_GLOCK_MISSEDTARGET]++; + } + + } else if (ftrace_data.tracepoint == GLOCK_PUT) { + char state[3]; + + sscanf(ftrace_data.data, + "gfs2_glock_put: %*d,%*d glock %*d:%*d state %*s => %s flags:%*s", + state + ); + + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKPUT_TOTAL]++; + + } else if (ftrace_data.tracepoint == DEMOTE_RQ) { + char state[3], remote[7]; + + sscanf(ftrace_data.data, + "gfs2_demote_rq: %*d,%*d glock %*d:%*d demote %*s to %s flags:%*s %s", + state, remote + ); + + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_DEMOTERQ_TOTAL]++; + + if (strncmp(remote, "remote", 6) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_REQUESTED_REMOTE]++; + } else if (strncmp(remote, "local", 6) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_REQUESTED_LOCAL]++; + } + + } else if (ftrace_data.tracepoint == PROMOTE) { + char state[3], first[6]; + + sscanf(ftrace_data.data, + "gfs2_promote: %*d,%*d glock %*d:%*d promote %s %s", + first, state + ); + + if (strncmp(first, "first", 5) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_EXCLUSIVE]++; + } + } else if (strncmp(first, "other", 5) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_EXCLUSIVE]++; + } + } + fs->ftrace.values[FTRACE_PROMOTE_TOTAL]++; + + } else if (ftrace_data.tracepoint == GLOCK_QUEUE) { + char state[3], queue[8]; + + sscanf(ftrace_data.data, + "gfs2_glock_queue: %*d,%*d glock %*d:%*d %s %s", + queue, state + ); + + if (strncmp(queue, "queue", 6) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_TOTAL]++; + + } else if (strncmp(queue, "dequeue", 8) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_TOTAL]++; + } + fs->ftrace.values[FTRACE_GLOCKQUEUE_TOTAL]++; + + } else if (ftrace_data.tracepoint == GLOCK_LOCK_TIME) { + uint32_t lock_type; + + sscanf(ftrace_data.data, + "gfs2_glock_lock_time: %*d,%*d glock %"SCNu32":%*d status:%*d flags:%*x tdiff:%*d srtt:%*d/%*d srttb:%*d/%*d sirt:%*d/%*d dcnt:%*d qcnt:%*d", + &lock_type + ); + + if (lock_type == 1) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_TRANS]++; + } else if (lock_type == 2) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_INDOE]++; + } else if (lock_type == 3) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_RGRP]++; + } else if (lock_type == 4) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_META]++; + } else if (lock_type == 5) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_IOPEN]++; + } else if (lock_type == 6) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_FLOCK]++; + } else if (lock_type == 8) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_QUOTA]++; + } else if (lock_type == 9) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_JOURNAL]++; + } + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_TOTAL]++; + + } else if (ftrace_data.tracepoint == PIN) { + char pinned[6]; + uint32_t length; + + sscanf(ftrace_data.data, + "gfs2_pin: %*d,%*d log %s %*d/%"SCNu32" inode: %*d", + pinned, &length + ); + + if (strncmp(pinned, "pin", 5) == 0) { + fs->ftrace.values[FTRACE_PIN_PINTOTAL]++; + } else if (strncmp(pinned, "unpin", 5) == 0) { + fs->ftrace.values[FTRACE_PIN_UNPINTOTAL]++; + } + fs->ftrace.values[FTRACE_PIN_TOTAL]++; + + if(fs->ftrace.values[FTRACE_PIN_LONGESTPINNED] < length) + fs->ftrace.values[FTRACE_PIN_LONGESTPINNED] = length; + + } else if (ftrace_data.tracepoint == LOG_FLUSH) { + char end[6]; + + sscanf(ftrace_data.data, + "gfs2_log_flush: %*d,%*d log flush %s %*u", + end + ); + + if (strncmp(end, "end", 6) == 0) + fs->ftrace.values[FTRACE_LOGFLUSH_TOTAL]++; + + } else if (ftrace_data.tracepoint == LOG_BLOCKS) { + + fs->ftrace.values[FTRACE_LOGBLOCKS_TOTAL]++; + + } else if (ftrace_data.tracepoint == AIL_FLUSH) { + char end[6]; + + sscanf(ftrace_data.data, + "gfs2_ail_flush: %*d,%*d ail flush %s %*s %*u", + end + ); + + if (strncmp(end, "end", 6) == 0) + fs->ftrace.values[FTRACE_AILFLUSH_TOTAL]++; + + } else if (ftrace_data.tracepoint == BLOCK_ALLOC) { + char type[8]; + + sscanf(ftrace_data.data, + "gfs2_block_alloc: %*d,%*d bmap %*u alloc %*u/%*u %s rg:%*u rf:%*u rr:%*u", + type + ); + + if (strncmp(type, "free", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_FREE]++; + } else if (strncmp(type, "used", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_USED]++; + } else if (strncmp(type, "dinode", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_DINODE]++; + } else if (strncmp(type, "unlinked", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_UNLINKED]++; + } + fs->ftrace.values[FTRACE_BLOCKALLOC_TOTAL]++; + + } else if (ftrace_data.tracepoint == BMAP) { + char type[8]; + + sscanf(ftrace_data.data, + "gfs2_bmap: %*d,%*d bmap %*u map %*u/%*u to %*u flags:%*x %s %*d", + type + ); + + if (strncmp(type, "create", 8) == 0) { + fs->ftrace.values[FTRACE_BMAP_CREATE]++; + } else if (strncmp(type, "nocreate", 8) == 0) { + fs->ftrace.values[FTRACE_BMAP_NOCREATE]++; + } + fs->ftrace.values[FTRACE_BMAP_TOTAL]++; + + } else if (ftrace_data.tracepoint == RS) { + char type[8]; + + sscanf(ftrace_data.data, + "gfs2_rs: %*d,%*d bmap %*u resrv %*u rg:%*u rf:%*u rr:%*u %s f:%*u", + type + ); + + if (strncmp(type, "del", 4) == 0) { + fs->ftrace.values[FTRACE_RS_DEL]++; + } else if (strncmp(type, "tdel", 4) == 0) { + fs->ftrace.values[FTRACE_RS_TDEL]++; + } else if (strncmp(type, "ins", 4) == 0) { + fs->ftrace.values[FTRACE_RS_INS]++; + } else if (strncmp(type, "clm", 4) == 0) { + fs->ftrace.values[FTRACE_RS_CLM]++; + } + fs->ftrace.values[FTRACE_RS_TOTAL]++; + + } + } + } +} + +/* + * We take all required data from the trace_pipe. Whilst keeping track of + * the number of locks we have seen so far. After locks have been collected + * we assign values and return. + */ +int +gfs2_refresh_ftrace_stats(pmInDom gfs_fs_indom) +{ + FILE *fp; + int fd, flags, reset_flag; + char buffer[8196]; + + /* Reset the metric types we have found */ + num_accepted_entries = 0; + reset_flag = 1; + + /* We open the pipe in both read-only and non-blocking mode */ + if ((fp = fopen(TRACE_PIPE, "r")) == NULL) + return -oserror(); + + /* Set flags of fp as non-blocking */ + fd = fileno(fp); + flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, flags | O_RDONLY | O_NONBLOCK) < 0) { + fclose(fp); + return -oserror(); + } + + /* Extract data from the trace_pipe */ + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + if (num_accepted_entries >= max_glock_throughput) + break; + + /* In the event of an allocation error */ + if (gfs2_extract_trace_values(buffer, gfs_fs_indom) != 0) + break; + + /* Processing here */ + gfs2_assign_ftrace(gfs_fs_indom, reset_flag); + } + + fclose(fp); + + /* Clear the rest of the ring buffer after passing max_glock_throughput */ + ftrace_clear_buffer(); + + return 0; +} diff --git a/src/pmdas/gfs2/ftrace.h b/src/pmdas/gfs2/ftrace.h new file mode 100644 index 0000000..7974783 --- /dev/null +++ b/src/pmdas/gfs2/ftrace.h @@ -0,0 +1,142 @@ +/* + * GFS2 ftrace based trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef FTRACE_H +#define FTRACE_H + +#define INITIAL_GLOBAL_MAX_GLOCK_THROUGHPUT 750000 + +enum { + GLOCK_STATE_CHANGE = 0, + GLOCK_PUT, + DEMOTE_RQ, + PROMOTE, + GLOCK_QUEUE, + GLOCK_LOCK_TIME, + PIN, + LOG_FLUSH, + LOG_BLOCKS, + AIL_FLUSH, + BLOCK_ALLOC, + BMAP, + RS, + NUM_FTRACE_TRACEPOINTS +}; + +enum { + FTRACE_GLOCKSTATE_TOTAL = 0, + FTRACE_GLOCKSTATE_NULLLOCK, + FTRACE_GLOCKSTATE_CONCURRENTREAD, + FTRACE_GLOCKSTATE_CONCURRENTWRITE, + FTRACE_GLOCKSTATE_PROTECTEDREAD, + FTRACE_GLOCKSTATE_PROTECTEDWRITE, + FTRACE_GLOCKSTATE_EXCLUSIVE, + FTRACE_GLOCKSTATE_GLOCK_CHANGEDTARGET, + FTRACE_GLOCKSTATE_GLOCK_MISSEDTARGET, + FTRACE_GLOCKPUT_TOTAL, + FTRACE_GLOCKPUT_NULLLOCK, + FTRACE_GLOCKPUT_CONCURRENTREAD, + FTRACE_GLOCKPUT_CONCURRENTWRITE, + FTRACE_GLOCKPUT_PROTECTEDREAD, + FTRACE_GLOCKPUT_PROTECTEDWRITE, + FTRACE_GLOCKPUT_EXCLUSIVE, + FTRACE_DEMOTERQ_TOTAL, + FTRACE_DEMOTERQ_NULLLOCK, + FTRACE_DEMOTERQ_CONCURRENTREAD, + FTRACE_DEMOTERQ_CONCURRENTWRITE, + FTRACE_DEMOTERQ_PROTECTEDREAD, + FTRACE_DEMOTERQ_PROTECTEDWRITE, + FTRACE_DEMOTERQ_EXCLUSIVE, + FTRACE_DEMOTERQ_REQUESTED_REMOTE, + FTRACE_DEMOTERQ_REQUESTED_LOCAL, + FTRACE_PROMOTE_TOTAL, + FTRACE_PROMOTE_FIRST_NULLLOCK, + FTRACE_PROMOTE_FIRST_CONCURRENTREAD, + FTRACE_PROMOTE_FIRST_CONCURRENTWRITE, + FTRACE_PROMOTE_FIRST_PROTECTEDREAD, + FTRACE_PROMOTE_FIRST_PROTECTEDWRITE, + FTRACE_PROMOTE_FIRST_EXCLUSIVE, + FTRACE_PROMOTE_OTHER_NULLLOCK, + FTRACE_PROMOTE_OTHER_CONCURRENTREAD, + FTRACE_PROMOTE_OTHER_CONCURRENTWRITE, + FTRACE_PROMOTE_OTHER_PROTECTEDREAD, + FTRACE_PROMOTE_OTHER_PROTECTEDWRITE, + FTRACE_PROMOTE_OTHER_EXCLUSIVE, + FTRACE_GLOCKQUEUE_TOTAL, + FTRACE_GLOCKQUEUE_QUEUE_TOTAL, + FTRACE_GLOCKQUEUE_QUEUE_NULLLOCK, + FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTREAD, + FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTWRITE, + FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDREAD, + FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDWRITE, + FTRACE_GLOCKQUEUE_QUEUE_EXCLUSIVE, + FTRACE_GLOCKQUEUE_DEQUEUE_TOTAL, + FTRACE_GLOCKQUEUE_DEQUEUE_NULLLOCK, + FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTREAD, + FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTWRITE, + FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDREAD, + FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDWRITE, + FTRACE_GLOCKQUEUE_DEQUEUE_EXCLUSIVE, + FTRACE_GLOCKLOCKTIME_TOTAL, + FTRACE_GLOCKLOCKTIME_TRANS, + FTRACE_GLOCKLOCKTIME_INDOE, + FTRACE_GLOCKLOCKTIME_RGRP, + FTRACE_GLOCKLOCKTIME_META, + FTRACE_GLOCKLOCKTIME_IOPEN, + FTRACE_GLOCKLOCKTIME_FLOCK, + FTRACE_GLOCKLOCKTIME_QUOTA, + FTRACE_GLOCKLOCKTIME_JOURNAL, + FTRACE_PIN_TOTAL, + FTRACE_PIN_PINTOTAL, + FTRACE_PIN_UNPINTOTAL, + FTRACE_PIN_LONGESTPINNED, + FTRACE_LOGFLUSH_TOTAL, + FTRACE_LOGBLOCKS_TOTAL, + FTRACE_AILFLUSH_TOTAL, + FTRACE_BLOCKALLOC_TOTAL, + FTRACE_BLOCKALLOC_FREE, + FTRACE_BLOCKALLOC_USED, + FTRACE_BLOCKALLOC_DINODE, + FTRACE_BLOCKALLOC_UNLINKED, + FTRACE_BMAP_TOTAL, + FTRACE_BMAP_CREATE, + FTRACE_BMAP_NOCREATE, + FTRACE_RS_TOTAL, + FTRACE_RS_DEL, + FTRACE_RS_TDEL, + FTRACE_RS_INS, + FTRACE_RS_CLM, + NUM_TRACEPOINT_STATS +}; + +struct ftrace { + uint64_t values[NUM_TRACEPOINT_STATS]; +}; + +struct ftrace_data { + dev_t dev_id; + int tracepoint; + char data[512]; +}; + +extern void ftrace_increase_num_accepted_entries(); +extern int gfs2_ftrace_fetch(int, struct ftrace *, pmAtomValue *); +extern int gfs2_refresh_ftrace_stats(pmInDom); + +extern int ftrace_get_threshold(); +extern int ftrace_set_threshold(pmValueSet *vsp); + +#endif /*FTRACE_H*/ diff --git a/src/pmdas/gfs2/glocks.c b/src/pmdas/gfs2/glocks.c new file mode 100644 index 0000000..e8dc81e --- /dev/null +++ b/src/pmdas/gfs2/glocks.c @@ -0,0 +1,93 @@ +/* + * GFS2 glocks sysfs file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "glocks.h" + +#include + +int +gfs2_glocks_fetch(int item, struct glocks *glocks, pmAtomValue *atom) +{ + /* Check for valid metric count */ + if (item < 0 || item >= NUM_GLOCKS_STATS) + return PM_ERR_PMID; + + /* Check for no values recorded */ + if(glocks->values[item] == UINT64_MAX) + return 0; + + atom->ull = glocks->values[item]; + return 1; +} + +int +gfs2_refresh_glocks(const char *sysfs, const char *name, struct glocks *glocks) +{ + char buffer[4096]; + FILE *fp; + + /* Reset all counter for this fs */ + memset(glocks, 0, sizeof(*glocks)); + + snprintf(buffer, sizeof(buffer), "%s/%s/glocks", sysfs, name); + buffer[sizeof(buffer)-1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL) { + /* + * We set the values to UINT64_MAX to signify we have no + * current values (no metric support or debugfs not mounted) + * + */ + memset(glocks, -1, sizeof(*glocks)); + return -oserror(); + } + + /* + * Read through glocks file accumulating statistics as we go; + * as an early starting point, we're simply binning aggregate + * glock state counts. + * + */ + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + char *p = buffer; + + /* interested in glock lines only for now */ + if (strncmp(p, "G:", 2) != 0) + continue; + for (p += 2; isspace((int)*p); p++) {;} + + /* pick out the various state fields next */ + if (strncmp(p, "s:SH", 4) == 0) + glocks->values[GLOCKS_SHARED]++; + else if (strncmp(p, "s:UN ", 4) == 0) + glocks->values[GLOCKS_UNLOCKED]++; + else if (strncmp(p, "s:DF ", 4) == 0) + glocks->values[GLOCKS_DEFERRED]++; + else if (strncmp(p, "s:EX", 4) == 0) + glocks->values[GLOCKS_EXCLUSIVE]++; + glocks->values[GLOCKS_TOTAL]++; + for (p += 4; isspace((int)*p); p++) {;} + + /* [ extract any other field stats here ] */ + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/gfs2/glocks.h b/src/pmdas/gfs2/glocks.h new file mode 100644 index 0000000..b56a574 --- /dev/null +++ b/src/pmdas/gfs2/glocks.h @@ -0,0 +1,36 @@ +/* + * GFS2 glock file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef GLOCKS_H +#define GLOCKS_H + +enum { + GLOCKS_TOTAL = 0, + GLOCKS_SHARED = 1, + GLOCKS_UNLOCKED = 2, + GLOCKS_DEFERRED = 3, + GLOCKS_EXCLUSIVE = 4, + NUM_GLOCKS_STATS +}; + +struct glocks { + __uint64_t values[NUM_GLOCKS_STATS]; +}; + +extern int gfs2_glocks_fetch(int, struct glocks *, pmAtomValue *); +extern int gfs2_refresh_glocks(const char *, const char *, struct glocks *); + +#endif /*GLOCKS_H*/ diff --git a/src/pmdas/gfs2/glstats.c b/src/pmdas/gfs2/glstats.c new file mode 100644 index 0000000..bcbd7b6 --- /dev/null +++ b/src/pmdas/gfs2/glstats.c @@ -0,0 +1,117 @@ +/* + * GFS2 glstats file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "glstats.h" + +/* + * GFS2 glock type identification; note that the glock type 7 is currently not + * used and is reserved to be on the safe side. + * + * Type Lock type Use + * 1 Trans Transaction lock + * 2 Inode Inode metadata and data + * 3 Rgrp Resource group metadata + * 4 Meta The superblock + * 5 Iopen Inode last closer detection + * 6 Flock flock(2) syscall + * 8 Quota Quota operations + * 9 Journal Journal mutex + * + */ + +int +gfs2_glstats_fetch(int item, struct glstats *glstats, pmAtomValue *atom) +{ + /* Handle the case for our reserved but not used glock type 7 */ + if ((item < 0 || item >= NUM_GLSTATS_STATS) && item != 7) + return PM_ERR_PMID; + + /* Check for no values recorded */ + if(glstats->values[item] == UINT64_MAX) + return 0; + + atom->ull = glstats->values[item]; + return 1; +} + +int +gfs2_refresh_glstats(const char *sysfs, const char *name, struct glstats *glstats){ + char buffer[4096]; + FILE *fp; + + /* Reset all counter for this fs */ + memset(glstats, 0, sizeof(*glstats)); + + snprintf(buffer, sizeof(buffer), "%s/%s/glstats", sysfs, name); + buffer[sizeof(buffer) - 1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL){ + /* + * We set the values to UINT64_MAX to signify we have no + * current values (no metric support or debugfs not mounted) + * + */ + memset(glstats, -1, sizeof(*glstats)); + return -oserror(); + } + + /* + * We read through the glstats file, finding out what glock types we are + * coming across and tally up the number of each type of glock we find. + * This file however contains the total number of locks at this time, + * on a large, heavy utilized filesystem there could be millions of entries + * so needs to be quick and efficient. + * + */ + while(fgets(buffer, sizeof(buffer), fp) != NULL){ + char *p = buffer; + + /* We pick out the various glock types by the identifying number */ + if (strncmp(p, "G: n:1", 6) == 0){ + glstats->values[GLSTATS_TRANS]++; + } else if (strncmp(p, "G: n:2 ", 6) == 0){ + glstats->values[GLSTATS_INODE]++; + } else if (strncmp(p, "G: n:3 ", 6) == 0){ + glstats->values[GLSTATS_RGRP]++; + } else if (strncmp(p, "G: n:4 ", 6) == 0){ + glstats->values[GLSTATS_META]++; + } else if (strncmp(p, "G: n:5 ", 6) == 0){ + glstats->values[GLSTATS_IOPEN]++; + } else if (strncmp(p, "G: n:6 ", 6) == 0){ + glstats->values[GLSTATS_FLOCK]++; + } else if (strncmp(p, "G: n:8 ", 6) == 0){ + glstats->values[GLSTATS_QUOTA]++; + } else if (strncmp(p, "G: n:9 ", 6) == 0){ + glstats->values[GLSTATS_JOURNAL]++; + } + glstats->values[GLSTATS_TOTAL]++; + + /* + * We advance the cursor for after we read what type of lock we have + * for (p += 6; isspace((int)*p); p++) {;} + * + * [ We can extract any other future fields from here on] + * + */ + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/gfs2/glstats.h b/src/pmdas/gfs2/glstats.h new file mode 100644 index 0000000..09e5264 --- /dev/null +++ b/src/pmdas/gfs2/glstats.h @@ -0,0 +1,57 @@ +/* + * GFS2 glstats file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef GLSTATS_H +#define GLSTATS_H + +/* + * GFS2 glock type identification; note that the glock type 7 is currently not + * used and is reserved to be on the safe side. + * + * Type Lock type Use + * 1 Trans Transaction lock + * 2 Inode Inode metadata and data + * 3 Rgrp Resource group metadata + * 4 Meta The superblock + * 5 Iopen Inode last closer detection + * 6 Flock flock(2) syscall + * 8 Quota Quota operations + * 9 Journal Journal mutex + * + */ + +enum { + GLSTATS_TOTAL = 0, + GLSTATS_TRANS = 1, + GLSTATS_INODE = 2, + GLSTATS_RGRP = 3, + GLSTATS_META = 4, + GLSTATS_IOPEN = 5, + GLSTATS_FLOCK = 6, + GLSTATS_RESERVED_NOT_USED = 7, + GLSTATS_QUOTA = 8, + GLSTATS_JOURNAL = 9, + NUM_GLSTATS_STATS +}; + +struct glstats { + __uint64_t values[NUM_GLSTATS_STATS]; +}; + +extern int gfs2_glstats_fetch(int, struct glstats *, pmAtomValue *); +extern int gfs2_refresh_glstats(const char *, const char *, struct glstats *); + +#endif /* GLSTATS_H */ diff --git a/src/pmdas/gfs2/help b/src/pmdas/gfs2/help new file mode 100644 index 0000000..350bd2b --- /dev/null +++ b/src/pmdas/gfs2/help @@ -0,0 +1,566 @@ +# +# Copyright (c) 2013 - 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# GFS2 PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# +@ GFS2.0 Instance domain for mounted GFS2 filesystems + +@ gfs2.glocks.total Count of total observed incore GFS2 global locks +Count of total incore GFS2 glock data structures based on parsing the contents +of the /sys/kernel/debug/gfs2//glocks files. + +@ gfs2.glocks.shared GFS2 global locks in shared state +Count of incore GFS2 glock data structures in shared state, based on parsing +/sys/kernel/debug/gfs2//glocks entries with state field (s:) value "SH". + +@ gfs2.glocks.unlocked GFS2 global locks in unlocked state +Count of incore GFS2 glock data structures in unlocked state, based on parsing +/sys/kernel/debug/gfs2//glocks entries with state field (s:) value "UN". + +@ gfs2.glocks.deferred GFS2 global locks in deferred state +Count of incore GFS2 glock data structures in deferred state, based on parsing +/sys/kernel/debug/gfs2//glocks entries with state field (s:) value "DF". + +@ gfs2.glocks.exclusive GFS2 global locks in exclusive state +Count of incore GFS2 glock data structures in exclusive state, based on parsing +/sys/kernel/debug/gfs2//glocks entries with state field (s:) value "EX". + +# help text for gfs2.sbstats.*.* is generated dynamically + +@ gfs2.glstats.total The total number of current glocks +Total count of the number of glocks which currently reside for filesystem on +the given node. Data is based from /sys/kernel/debug/gfs2//glstats +counting the total number of glock entries. + +@ gfs2.glstats.trans The number of transaction glocks +The count of the current number of transaction type glocks that currently exist +for the given filesystem. The data is recorded and counted from /sys/kernel/ +debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.inode The number of inode (metadata and data) glocks +The count of the current number of inode metadata and data type glocks that +currently exist for the given filesystem. The data is recorded and counted from +/sys/kernel/debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.rgrp The number of resource group metadata glocks +The count of the current number of resource group metadata type glocks that +currently exist for the given filesystem. The data is recorded and counted from +/sys/kernel/debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.meta The number of superblock glocks +The count of the current number of superblock type glocks that currently exist +for the given filesystem. The data is recorded and counted from /sys/kernel/ +debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.iopen The number of inode last closer detection glocks +The count of the current number of inode last closer detection type glocks that +currently exist for the given filesystem. The data is recorded and counted from +/sys/kernel/debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.flock The number of flock(2) syscall glocks +The count of the current number of flock(2) syscall type glocks that currently +exist for the given filesystem. The data is recorded and counted from /sys/ +kernel/debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.quota The number of quota operations glocks +The count of the current number of quota operations type glocks that currently +exist for the given filesystem. The data is recorded and counted from /sys/ +kernel/debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.glstats.journal The number of journal mutex glocks +The count of the current number of journal mutex type glocks that currently +exist for the given filesystem. The data is recorded and counted from /sys/ +kernel/debug/gfs2/glstats file entries for this type of glock. + +@ gfs2.tracepoints.glock_state_change.total Total number of glock state +changes. The total number of counted glock state changes. + +@ gfs2.tracepoints.glock_state_change.null_lock Number of null_lock state +changes. The total number of glock state changes to the null_lock state. + +@ gfs2.tracepoints.glock_state_change.concurrent_read Number of +concurrent_read state changes. The total number of glock state changes +to current_read state. + +@ gfs2.tracepoints.glock_state_change.concurrent_write Number of +concurrent_write state changes. The total number of glock state changes +to current_write state. + +@ gfs2.tracepoints.glock_state_change.protected_read Number of +protected_read state changes. The total number of glock state changes to +protected_read state. + +@ gfs2.tracepoints.glock_state_change.protected_write Number of +protected_write state changes. The total number of glock state changes to +protected_write state. + +@ gfs2.tracepoints.glock_state_change.exclusive Number of exclusive state +changes. The total number of glock state changes to exclusive state. + +@ gfs2.tracepoints.glock_state_change.glocks.changed_target Number of +changed locks. The number of state changes that achieved their expected +state change. + +@ gfs2.tracepoints.glock_state_change.glocks.missed_target Number of +missed locks. The number of state changes that did not achieve their +expected state change. + +@ gfs2.tracepoints.glock_put.total Total number of glocks changed. +The total number of glocks that have been changed. + +@ gfs2.tracepoints.glock_put.null_lock Number of released locks. +The number of glocks put into the null_lock state. + +@ gfs2.tracepoints.glock_put.concurrent_read Number of glocks put +in concurrent_read. The number of glocks put into the concurrent_read +state. + +@ gfs2.tracepoints.glock_put.concurrent_write Number of glocks put +in concurrent_write. The number of glocks put into the concurrent_write +state. + +@ gfs2.tracepoints.glock_put.protected_read Number of glocks put +in protected_read. The number of glocks put into the protected_read +state. + +@ gfs2.tracepoints.glock_put.protected_write Number of glocks put +in protected_wirte. The number of glocks put into the protected_write +state. + +@ gfs2.tracepoints.glock_put.exclusive Number of glocks put +in exclusive. The number of glocks put into the exclusive +state. + +@ gfs2.tracepoints.demote_rq.total Total number of lock demote requests. +The total number of lock demotion requests. + +@ gfs2.tracepoints.demote_rq.null_lock Number of lock demote requests to +null_lock. The total number of lock demotion requests to the null_lock state. + +@ gfs2.tracepoints.demote_rq.concurrent_read Number of lock demote requests to +concurrent_read. The total number of lock demotion requests to the +concurrent_read state. + +@ gfs2.tracepoints.demote_rq.concurrent_write Number of lock demote requests to +concurrent_write. The total number of lock demotion requests to the +concurrent_write state. + +@ gfs2.tracepoints.demote_rq.protected_read Number of lock demote requests to +protected_read. The total number of lock demotion requests to the +protected_read state. + +@ gfs2.tracepoints.demote_rq.protected_write Number of lock demote requests to +protected_write. The total number of lock demotion requests to the +protected_write state. + +@ gfs2.tracepoints.demote_rq.exclusive Number of lock demote requests to +exclusive. The total number of lock demotion requests to the +exclusive state. + +@gfs2.tracepoints.demote_rq.requested.remote Number of demote requests (remote). +The total number of demote requests which were requested by a remote node of +the cluster. + +@gfs2.tracepoints.demote_rq.requested.local Number of demote requests (local). +The total number of demote requests which were requested by a local node of +the cluster + +@ gfs2.tracepoints.promote.total Total number of lock state. The total number +of lock state. + +@ gfs2.tracepoints.promote.first.null_lock Number of lock state to null_lock. +The total number of successful first time lock state to the null_lock state. + +@ gfs2.tracepoints.promote.first.concurrent_read Number of lock state to +concurrent_read. The total number of successful first time lock state to the +concurrent_read state. + +@ gfs2.tracepoints.promote.first.concurrent_write Number of lock state to +concurrent_write. The total number of successful first time lock state to the +concurrent_write state. + +@ gfs2.tracepoints.promote.first.protected_read Number of lock state to +protected_read. The total number of successful first time lock state to the +protected_read state. + +@ gfs2.tracepoints.promote.first.protected_write Number of lock state to +protected_write. The total number of successful first time lock state to the +protected_write state. + +@ gfs2.tracepoints.promote.first.exclusive Number of lock state to +exclusive. The total number of successful first time lock state to the +exclusive state. + +@ gfs2.tracepoints.promote.other.null_lock Number of lock state to null_lock. +The total number of successful other time lock state to the null_lock state. + +@ gfs2.tracepoints.promote.other.concurrent_read Number of lock state to +concurrent_read. The total number of successful other time lock state to the +concurrent_read state. + +@ gfs2.tracepoints.promote.other.concurrent_write Number of lock state to +concurrent_write. The total number of successful other time lock state to the +concurrent_write state. + +@ gfs2.tracepoints.promote.other.protected_read Number of lock state to +protected_read. The total number of successful other time lock state to the +protected_read state. + +@ gfs2.tracepoints.promote.other.protected_write Number of lock state to +protected_write. The total number of successful other time lock state to the +protected_write state. + +@ gfs2.tracepoints.promote.other.exclusive Number of lock state to +exclusive. The total number of successful other time lock state to the +exclusive state. + +@ gfs2.tracepoints.glock_queue.total Total numbe rof queued and dequeued +requests. The total number of both queued and dequeued requests. + +@ gfs2.tracepoints.glock_queue.queue.total Total number of queued lock requests. +The total number of queued lock requests. + +@ gfs2.tracepoints.glock_queue.queue.null_lock Number of null_lock requests. The +number of lock requests to the null_lock state. + +@ gfs2.tracepoints.glock_queue.queue.concurrent_read Number of concurrent_read +requests. The number of lock requests to the concurrent_read state. + +@ gfs2.tracepoints.glock_queue.queue.concurrent_write Number of concurrent_write +requests. The number of lock requests to the concurrent_write state. + +@ gfs2.tracepoints.glock_queue.queue.protected_read Number of protected_read +requests. The number of lock requests to the protected_read state. + +@ gfs2.tracepoints.glock_queue.queue.protected_write Number of protected_write +requests. The number of lock requests to the protected_write state. + +@ gfs2.tracepoints.glock_queue.queue.exclusive Number of exclusive +requests. The number of lock requests to the exclusive state. + +@ gfs2.tracepoints.glock_queue.dequeue.total Total number of dequeued lock requests. +The total number of dequeued lock requests. + +@ gfs2.tracepoints.glock_queue.dequeue.null_lock Number of null_lock requests. The +number of lock requests to the null_lock state. + +@ gfs2.tracepoints.glock_queue.dequeue.concurrent_read Number of concurrent_read +requests. The number of lock requests to the concurrent_read state. + +@ gfs2.tracepoints.glock_queue.dequeue.concurrent_write Number of concurrent_write +requests. The number of lock requests to the concurrent_write state. + +@ gfs2.tracepoints.glock_queue.dequeue.protected_read Number of protected_read +requests. The number of lock requests to the protected_read state. + +@ gfs2.tracepoints.glock_queue.dequeue.protected_write Number of protected_write +requests. The number of lock requests to the protected_write state. + +@ gfs2.tracepoints.glock_queue.dequeue.exclusive Number of exclusive +requests. The number of lock requests to the exclusive state. + +@ gfs2.tracepoints.glock_lock_time.total Total number of lock updates. +The total number of lock updates. + +@ gfs2.tracepoints.glock_lock_time.trans Number of transaction lock updates. +The number of updates for transaction based glocks. + +@ gfs2.tracepoints.glock_lock_time.inode Number of inode lock updates. +The number of updates for inode based glocks. + +@ gfs2.tracepoints.glock_lock_time.rgrp Number of resource group lock updates. +The number of updates for resource group based glocks. + +@ gfs2.tracepoints.glock_lock_time.meta Number of metadata lock updates. +The number of updates for metadata based glocks. + +@ gfs2.tracepoints.glock_lock_time.iopen Number of iopen lock updates. +The number of updates for iopen based glocks. + +@ gfs2.tracepoints.glock_lock_time.flock Number of flock lock updates. +The number of updates for flock based glocks. + +@ gfs2.tracepoints.glock_lock_time.quota Number of quota lock updates. +The number of updates for quota based glocks. + +@ gfs2.tracepoints.glock_lock_time.journal Number of journal lock updates. +The number of updates for journal based glocks. + +@ gfs2.tracepoints.pin.total Total number of Pin/Unpin requests. The total +number of requests to pin/unpin blocks on the log. + +@ gfs2.tracepoints.pin.pin_total Number of pin requests. The total number of +requests to pin blocks on the log. + +@ gfs2.tracepoints.pin.unpin_total Number of unpin requests. The total number +requests to unpin blocks on the log. + +@ gfs2.tracepoints.pin.longest_pinned Longest pinned. The longest pinned +inode or resource group log block + +@ gfs2.tracepoints.log_flush.total Total log flushes. The total number of +log flushes observed + +@ gfs2.tracepoints.log_block.total Total log blocks. The total number of +blocks placed upon the log. + +@ gfs2.tracepoints.ail_flush.total Total AIL flushes. The total number of +flushes back to the AIL. + +@ gfs2.tracepoints.block_alloc.total Total blocks allocated/deallocated. +The total number of allocated/freed blocks this call. + +@ gfs2.tracepoints.block_alloc.free Freed blocks. The number of blocks +freed. + +@ gfs2.tracepoints.block_alloc.used Used blocks. The number of blocks +used. + +@ gfs2.tracepoints.block_alloc.dinode Dinode blocks. The number of blocks +used for dinode. + +@ gfs2.tracepoints.block_alloc.unlinked Unlinked blocks. The number of +unlinked blocks. + +@ gfs2.tracepoints.bmap.total Total number of bmap allocations. The total +number of bmap allocations. + +@ gfs2.tracepoints.bmap.create Number of create bmap allocations. The number +of create bmap allocations. + +@ gfs2.tracepoints.bmap.nocreate Number of nocreate bmap allocations. The +number of nocreate bmap allocations. + +@ gfs2.tracepoints.rs.total Total multi-block allocations. The total number +of multi-block allocations. + +@ gfs2.tracepoints.rs.del Number of resource group delete. The total number of +resource group delete calls. + +@ gfs2.tracepoints.rs.tdel Number of resource group tree delete. The total number +of resource group tree delete calls. + +@ gfs2.tracepoints.rs.ins Number of resource group insert. The total number of +resource group insert calls. + +@ gfs2.tracepoints.rs.clm Number of resource group claims. The total number of +resource group claim calls. + +# help text for gfs2.worst_glock.*.* is generated dynamically + +@ gfs2.latency.grant.all Average time in ms for all states. The total average +latency time in ms for all lock states for grants. + +@ gfs2.latency.grant.null_lock Average time in ms to null lock state. The +total average latency time in ms to change to null lock state for grants. + +@ gfs2.latency.grant.concurrent_read Average time in ms to concurrent read +lock state. The total average latency time in ms to change to concurrent read +lock state for grants. + +@ gfs2.latency.grant.concurrent_write Average time in ms to concurrent write +lock state. The total average latency time in ms to change to concurrent write +lock state for grants. + +@ gfs2.latency.grant.protected_read Average time in ms to protected read +lock state. The total average latency time in ms to change to protected read +lock state for grants. + +@ gfs2.latency.grant.protected_write Average time in ms to protected write +lock state. The total average latency time in ms to change to protected write +lock state for grants. + +@ gfs2.latency.grant.exclusive Average time in ms to exclusive lock state. The +total average latency time in ms to change to exclusive lock state for grants. + +@ gfs2.latency.demote.all Average time in ms for all states. The total average +latency time in ms for all lock states for demotes. + +@ gfs2.latency.demote.null_lock Average time in ms to null lock state. The +total average latency time in ms to change to null lock state for demotes. + +@ gfs2.latency.demote.concurrent_read Average time in ms to concurrent read +lock state. The total average latency time in ms to change to concurrent read +lock state for demotes. + +@ gfs2.latency.demote.concurrent_write Average time in ms to concurrent write +lock state. The total average latency time in ms to change to concurrent write +lock state for demotes. + +@ gfs2.latency.demote.protected_read Average time in ms to protected read +lock state. The total average latency time in ms to change to protected read +lock state for demotes. + +@ gfs2.latency.demote.protected_write Average time in ms to protected write +lock state. The total average latency time in ms to change to protected write +lock state for demotes. + +@ gfs2.latency.demote.exclusive Average time in ms to exclusive lock state. +The total average latency time in ms to change to exclusive lock state for +demotes. + +@ gfs2.latency.queue.all Average time in ms for all states. The total average +latency time in ms for all lock states for queues. + +@ gfs2.latency.queue.null_lock Average time in ms to null lock state. The +total average latency time in ms to change to null lock state for queues. + +@ gfs2.latency.queue.concurrent_read Average time in ms to concurrent read +lock state. The total average latency time in ms to change to concurrent read +lock state for queues. + +@ gfs2.latency.queue.concurrent_write Average time in ms to concurrent write +lock state. The total average latency time in ms to change to concurrent write +lock state for queues. + +@ gfs2.latency.queue.protected_read Average time in ms to protected read +lock state. The total average latency time in ms to change to protected read +lock state for queues. + +@ gfs2.latency.queue.protected_write Average time in ms to protected write +lock state. The total average latency time in ms to change to protected write +lock state for queues. + +@ gfs2.latency.queue.exclusive Average time in ms to exclusive lock state. +The total average latency time in ms to change to exclusive lock state for +queues. + +@ gfs2.control.tracepoints.all Indication whether glock statistics are enabled +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.all 0 [off] or 1 [on]. Setting the value of the metric +controls the behavior of the PMDA to whether it tries to collect from tracepoint +metrics or not. + +@ gfs2.control.tracepoints.glock_state_change Indication whether +glock_state_change glock stats are enabled. The gfs2 tracepoint statistics +can be manually controlled using pmstore +gfs2.control.tracepoints.glock_state_change 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.glock_put Indication whether glock_put glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.glock_put 0 [off] or 1 [on]. Setting the value +of the metric controls the behavior of the PMDA to whether it tries to collect +from tracepoint metrics or not. + +@ gfs2.control.tracepoints.demote_rq Indication whether glock_demote_rq glock +stats are enabled. The gfs2 tracepoint statistics can be manually controlled +using pmstore gfs2.control.tracepoints.glock_demote_rq 0 [off] or 1 [on]. +Setting the value of the metric controls the behavior of the PMDA to whether +it tries to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.promote Indication whether glock_promote glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.glock_promte 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.glock_queue Indication whether glock_queue glock +stats are enabled. The gfs2 tracepoint statistics can be manually controlled +using pmstore gfs2.control.tracepoints.glock_queue 0 [off] or 1 [on]. Setting +the value of the metric controls the behavior of the PMDA to whether it tries +to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.glock_lock_time Indication whether glock_lock_time +glock stats are enabled. The gfs2 tracepoint statistics can be manually +controlled using pmstore gfs2.control.tracepoints.glock_lock_time 0 [off] or 1 +[on]. Setting the value of the metric controls the behavior of the PMDA to +whether it tries to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.pin Indication whether pin glock stats are enabled. +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.pin 0 [off] or 1 [on]. Setting the value of the +metric controls the behavior of the PMDA to whether it tries to collect from +tracepoint metrics or not. + +@ gfs2.control.tracepoints.log_flush Indication whether log_flush glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.log_flush 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.log_blocks Indication whether log_blocks glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.log_blocks 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.ail_flush Indication whether ail_flush glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.ail_flush 0 [off] or 1 [on]. Setting the value +of the metric controls the behavior of the PMDA to whether it tries to collect +from tracepoint metrics or not. + +@ gfs2.control.tracepoints.block_alloc Indication whether block_alloc glock +stats are enabled. The gfs2 tracepoint statistics can be manually controlled +using pmstore gfs2.control.tracepoints.block_alloc 0 [off] or 1 [on]. Setting +the value of the metric controls the behavior of the PMDA to whether it tries +to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.bmap Indication whether bmap glock stats are enabled. +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.bmap 0 [off] or 1 [on]. Setting the value of the +metric controls the behavior of the PMDA to whether it tries to collect from +tracepoint metrics or not. + +@ gfs2.control.tracepoints.rs Indication whether rs glock stats are enabled. +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.rs 0 [off] or 1 [on]. Setting the value of the metric +controls the behavior of the PMDA to whether it tries to collect from +tracepoint metrics or not. + +@ gfs2.control.buffer_size_kb Sets the buffer size for trace_pipe (per cpu). +The size of the trace_pipe buffer can be controlled with this metrics, it +allows the increase of the trace_pipe buffer to 128MB (131072KB) per cpu +on the system. It is useful to increase the size of the buffer when there +is expected to be heavy load on the file system in order to reduce the +risk of overwritten entries in the trace_pipe before they are read (default +value is 32MB (32768KB). + +@ gfs2.control.global_tracing Indication whether global tracing is enabled. +The global tracing can be controlled using pmstore gfs2.control.global_tracing +0 [off] or 1 [on]. This is required to be on for most of the gfs2 metrics to +function. + +@ gfs2.control.worst_glock Indication whether gfs2.glock_lock_time statistics +are enabled. The gfs2.glock_lock_time statistics can be manually controlled +using pmstore gfs2.control.glock_lock_time 0 [off] or 1 [on]. Setting the value +of the metric controls the behavior of the PMDA to whether it tries to collect +the lock_time metrics or not. The machine must have the gfs2 trace-points +available for the glock_lock_time based metrics to function. + +@ gfs2.control.latency Indication whether gfs2.latency statistics are enabled. +The gfs2.latency statistics can be manually controlled using pmstore +gfs2.control.latency 0 [off] or 1 [on]. Setting the value of the metric +controls the behaviour of the PMDA to whether it tries to collect the latency +metrics or not. The machice must have the gfs2 trace-points available for the +latency metrics to function. + +@ gfs2.control.glock_threshold Threshold for maximum number of glocks accepted +per fetch. The number of glocks that will be processed and accepted over all +ftrace read trace statistics. This number can be manually altered using pmstore +in order to tailor the number of glocks processed. This value must be positive. diff --git a/src/pmdas/gfs2/latency.c b/src/pmdas/gfs2/latency.c new file mode 100644 index 0000000..0b93ca9 --- /dev/null +++ b/src/pmdas/gfs2/latency.c @@ -0,0 +1,373 @@ +/* + * GFS2 latency metrics. + * + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "pmdagfs2.h" + +#include "ftrace.h" +#include "worst_glock.h" +#include "latency.h" + +#include +#include +#include +#include + +static struct ftrace_data latency_data; +static int reset_flag; + +static int latency_state = DEFAULT_LATENCY_STATE; + +/* + * Calculates the offset position for the flat array as if it was + * a three dimentional array containing the latency values. + */ +int +offset(int x, int y, int z) +{ + return (z * NUM_LATENCY_STATS * NUM_LATENCY_VALUES) + (y * NUM_LATENCY_STATS) + x ; +} + +/* + * Fetches the value for the given metric item and then assigns to pmAtomValue. + * We check to see if item is in valid range for the metric. + */ +int +gfs2_latency_fetch(int item, struct latency *latency, pmAtomValue *atom) +{ + int i, counter, position, results_used = 0; + int64_t result = 0; + + /* We are assigning values so we want to reset on next extract */ + reset_flag = 1; + + /* Ensure that metric value wanted is valid */ + if ((item < 0 || item >= NUM_LATENCY_STATS)) + return PM_ERR_PMID; + + counter = latency->counter[item]; + + /* Calculate latency for the metric (deduct start time and add the matching finish time) */ + for (i = 0; i < counter; i++) { + position = offset(item, i, END); + result += latency->values[position].usecs; + + position = offset(item, i, START); + result -= latency->values[position].usecs; + + results_used++; + } + /* If we have no values return no values */ + if (results_used == 0) + return 0; + + /* Return no values if negative result */ + if (result < 0 ) + return 0; + + /* Divide final value by number of counts */ + result /= results_used; + + /* Assign value to the metric */ + atom->ll = result; + + return 1; +} + +/* + * Sets the value of latency using pmstore, value + * must be 0 or 1. + */ +int +latency_set_state(pmValueSet *vsp) +{ + int value = vsp->vlist[0].value.lval; + + if (value == 0 || value == 1) { + latency_state = value; + + return 0; + } else { + return PM_ERR_SIGN; + } +} + +/* + * Used to see whether the latency metrics are enabled or disabled. Should + * only return either 0 or 1. + */ +int +latency_get_state() +{ + return latency_state; +} + +/* + * Concatenates the ftrace time stamp (major and minor) components into one + * uint64_t timestamp in usecs. + * + */ +static int64_t +concatenate(int64_t a, int64_t b) +{ + unsigned int power = 10; + + while(a >= power) + power *= 10; + + return a * power + b; +} + +/* + * Converts lock state to an integer + */ +static int +lock_to_decimal(char *state) +{ + if (strncmp(state, "NL", 2) == 0) { + return 0; + } else if (strncmp(state, "CR", 2) == 0) { + return 1; + } else if (strncmp(state, "CW", 2) == 0) { + return 2; + } else if (strncmp(state, "PR", 2) == 0) { + return 3; + } else if (strncmp(state, "PW", 2) == 0) { + return 4; + } else if (strncmp(state, "EX", 2) == 0) { + return 5; + } + + return 0; +} + +/* + * Updates the records held in the fs->latency structure with new latency + * stats. + */ +static int +update_records(struct gfs2_fs *fs, int metric, struct latency_data data, int placement) +{ + int i, position, counter; + struct latency_data blank = { 0 }; + + counter = fs->latency.counter[metric]; + + /* If we have an intial value */ + if (placement == START) { + position = offset(metric, counter, START); + fs->latency.values[position] = data; + + position = offset(metric, counter, END); + fs->latency.values[position] = blank; + + fs->latency.counter[metric] = (counter + 1) % NUM_LATENCY_VALUES; + + /* If we have a final value */ + } else if (placement == END) { + for (i = 0; i < counter; i++){ + position = offset(metric, i, START); + + if ((fs->latency.values[position].lock_type == data.lock_type) && + (fs->latency.values[position].number == data.number) && + (fs->latency.values[position].usecs < data.usecs )) { + + position = offset(metric, i, END); + fs->latency.values[position] = data; + + return 0; + } + } + } + return 0; +} + +/* + * We work out the individual metric values from our buffer input and store + * them for processing after all of the values have been extracted from the + * trace pipe. + */ +int +gfs2_extract_latency(unsigned int major, unsigned int minor, int tracepoint, char *data, pmInDom gfs_fs_indom) +{ + latency_data.dev_id = makedev(major, minor); + latency_data.tracepoint = tracepoint; + strncpy(latency_data.data, data, sizeof(latency_data.data)-1); + + int i, sts; + struct gfs2_fs *fs; + + /* We walk through for each filesystem */ + for (pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + sts = pmdaCacheLookup(gfs_fs_indom, i, NULL, (void **)&fs); + if (sts != PMDA_CACHE_ACTIVE) + continue; + + /* Clear old entries if reset is set */ + if (reset_flag == 1) { + memset(fs->latency.values, 0, sizeof fs->latency.values); + memset(fs->latency.counter, 0, sizeof fs->latency.counter); + reset_flag = 0; + } + + /* Is the entry matching the filesystem we are on? */ + if (fs->dev_id != latency_data.dev_id) + continue; + + if (latency_data.tracepoint == GLOCK_QUEUE) { + + struct latency_data data; + int64_t time_major, time_minor; + char queue[8], state[3]; + + sscanf(latency_data.data, + "%*s [%*d] %"SCNd64".%"SCNd64": gfs2_glock_queue: %*d,%*d glock %"SCNu32":%"SCNu64" %s %s", + &time_major, &time_minor, &data.lock_type, &data.number, queue, state + ); + data.usecs = concatenate(time_major, time_minor); + + if (data.lock_type == WORSTGLOCK_INODE || data.lock_type == WORSTGLOCK_RGRP) { + + /* queue trace data is used both for latency.grant and latency.queue */ + if (strncmp(queue, "queue", 6) == 0) { + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_GRANT_NL, data, START); + update_records(fs, LATENCY_QUEUE_NL, data, START); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_GRANT_CR, data, START); + update_records(fs, LATENCY_QUEUE_CR, data, START); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_GRANT_CW, data, START); + update_records(fs, LATENCY_QUEUE_CW, data, START); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_GRANT_PR, data, START); + update_records(fs, LATENCY_QUEUE_PR, data, START); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_GRANT_PW, data, START); + update_records(fs, LATENCY_QUEUE_PW, data, START); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_GRANT_EX, data, START); + update_records(fs, LATENCY_QUEUE_EX, data, START); + } + update_records(fs, LATENCY_GRANT_ALL, data, START); + update_records(fs, LATENCY_QUEUE_ALL, data, START); + + } else if (strncmp(queue, "dequeue", 8) == 0) { + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_QUEUE_NL, data, END); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_QUEUE_CR, data, END); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_QUEUE_CW, data, END); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_QUEUE_PR, data, END); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_QUEUE_PW, data, END); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_QUEUE_EX, data, END); + } + update_records(fs, LATENCY_QUEUE_ALL, data, END); + } + } + } else if (latency_data.tracepoint == GLOCK_STATE_CHANGE) { + + struct latency_data data; + int64_t time_major, time_minor; + char state[3], to[3], target[3]; + int state_decimal, to_decimal; + + sscanf(latency_data.data, + "%*s [%*d] %"SCNd64".%"SCNd64": gfs2_glock_state_change: %*d,%*d glock %"SCNu32":%"SCNu64" state %s to %s tgt:%s dmt:%*s flags:%*s", + &time_major, &time_minor, &data.lock_type, &data.number, state, to, target + ); + data.usecs = concatenate(time_major, time_minor); + state_decimal = lock_to_decimal(state); + to_decimal = lock_to_decimal(to); + + if (data.lock_type == WORSTGLOCK_INODE || data.lock_type == WORSTGLOCK_RGRP) { + + /* state change trace data is used both for latency.grant and latency.demote */ + if ((state_decimal < to_decimal) && (strncmp(to, target, 2) == 0)) { + if (strncmp(to, "NL", 2) == 0) { + update_records(fs, LATENCY_GRANT_NL, data, END); + } else if (strncmp(to, "CR", 2) == 0) { + update_records(fs, LATENCY_GRANT_CR, data, END); + } else if (strncmp(to, "CW", 2) == 0) { + update_records(fs, LATENCY_GRANT_CW, data, END); + } else if (strncmp(to, "PR", 2) == 0) { + update_records(fs, LATENCY_GRANT_PR, data, END); + } else if (strncmp(to, "PW", 2) == 0) { + update_records(fs, LATENCY_GRANT_PW, data, END); + } else if (strncmp(to, "EX", 2) == 0) { + update_records(fs, LATENCY_GRANT_EX, data, END); + } + update_records(fs, LATENCY_GRANT_ALL, data, END); + + } else if ((state_decimal > to_decimal) && (strncmp(to, target, 2) == 0)) { + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_NL, data, END); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CR, data, END); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CW, data, END); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PR, data, END); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PW, data, END); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_EX, data, END); + } + update_records(fs, LATENCY_DEMOTE_ALL, data, END); + } + } + } else if (latency_data.tracepoint == DEMOTE_RQ) { + + struct latency_data data; + int64_t time_major, time_minor; + char state[3]; + + sscanf(latency_data.data, + "%*s [%*d] %"SCNd64".%"SCNd64": gfs2_demote_rq: %*d,%*d glock %"SCNu32":%"SCNu64" demote %s to %*s flags:%*s %*s", + &time_major, &time_minor, &data.lock_type, &data.number, state + ); + data.usecs = concatenate(time_major, time_minor); + + if ((data.lock_type == WORSTGLOCK_INODE) || (data.lock_type == WORSTGLOCK_RGRP)) { + + /* demote rq trace data is used for latency.demote */ + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_NL, data, START); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CR, data, START); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CW, data, START); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PR, data, START); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PW, data, START); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_EX, data, START); + } + update_records(fs, LATENCY_DEMOTE_ALL, data, START); + } + } + } + return 0; +} diff --git a/src/pmdas/gfs2/latency.h b/src/pmdas/gfs2/latency.h new file mode 100644 index 0000000..aa2a1ef --- /dev/null +++ b/src/pmdas/gfs2/latency.h @@ -0,0 +1,70 @@ +/* + * GFS2 latency metrics. + * + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef LATENCY_H +#define LATENCY_H + +#define DEFAULT_LATENCY_STATE 1 +#define NUM_LATENCY_VALUES 512 + +enum{ + START = 0, + END +}; + +enum { + LATENCY_GRANT_ALL = 0, + LATENCY_GRANT_NL, + LATENCY_GRANT_CR, + LATENCY_GRANT_CW, + LATENCY_GRANT_PR, + LATENCY_GRANT_PW, + LATENCY_GRANT_EX, + LATENCY_DEMOTE_ALL, + LATENCY_DEMOTE_NL, + LATENCY_DEMOTE_CR, + LATENCY_DEMOTE_CW, + LATENCY_DEMOTE_PR, + LATENCY_DEMOTE_PW, + LATENCY_DEMOTE_EX, + LATENCY_QUEUE_ALL, + LATENCY_QUEUE_NL, + LATENCY_QUEUE_CR, + LATENCY_QUEUE_CW, + LATENCY_QUEUE_PR, + LATENCY_QUEUE_PW, + LATENCY_QUEUE_EX, + NUM_LATENCY_STATS +}; + +struct latency_data { + uint32_t lock_type; + uint64_t number; + int64_t usecs; +}; + +struct latency { + struct latency_data values [NUM_LATENCY_STATS * NUM_LATENCY_VALUES * 2]; /* START and STOP values */ + int counter [NUM_LATENCY_STATS]; +}; + +extern int gfs2_latency_fetch(int, struct latency *, pmAtomValue *); +extern int gfs2_extract_latency(unsigned int, unsigned int, int, char *, pmInDom); + +extern int latency_get_state(); +extern int latency_set_state(pmValueSet *vsp); + +#endif /* LATENCY_H */ diff --git a/src/pmdas/gfs2/pmda.c b/src/pmdas/gfs2/pmda.c new file mode 100644 index 0000000..2090296 --- /dev/null +++ b/src/pmdas/gfs2/pmda.c @@ -0,0 +1,1063 @@ +/* + * Global Filesystem v2 (GFS2) PMDA + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" + +#include "pmdagfs2.h" + +#include +#include + +static char *gfs2_sysfsdir = "/sys/kernel/debug/gfs2"; +static char *gfs2_sysdir = "/sys/fs/gfs2"; + +pmdaIndom indomtable[] = { + { .it_indom = GFS_FS_INDOM }, +}; + +#define INDOM(x) (indomtable[x].it_indom) + +/* + * all metrics supported in this PMDA - one table entry for each + * + */ +pmdaMetric metrictable[] = { + /* GLOCK */ + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_SHARED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_UNLOCKED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_DEFERRED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + /* SBSTATS */ + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTTVAR), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTTB), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTTVARB), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SIRT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SIRTVAR), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_DCOUNT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_QCOUNT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* GLSTATS */ + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_TRANS), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_INODE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_RGRP), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_META), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_IOPEN), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_FLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_QUOTA), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_JOURNAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* TRACEPOINTS */ + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_GLOCK_CHANGEDTARGET), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_GLOCK_MISSEDTARGET), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_REQUESTED_REMOTE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_REQUESTED_LOCAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_TRANS), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_INDOE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_RGRP), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_META), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_IOPEN), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_FLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_QUOTA), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_JOURNAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_PINTOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_UNPINTOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_LONGESTPINNED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_LOGFLUSH_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_LOGBLOCKS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_AILFLUSH_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_FREE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_USED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_DINODE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_UNLINKED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BMAP_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BMAP_CREATE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BMAP_NOCREATE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_DEL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_TDEL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_INS), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_CLM), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* WORST_GLOCKS */ + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_LOCK_TYPE), + PM_TYPE_U32, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_NUMBER), + PM_TYPE_U32, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTT), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTTVAR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTTB), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTTVARB), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SIRT), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SIRTVAR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_DLM), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_QUEUE), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* LATENCY */ + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_ALL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_NL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_CR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_CW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_PR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_PW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_EX), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_ALL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_NL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_CR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_CW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_PR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_PW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_EX), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_ALL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_NL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_CR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_CW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_PR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_PW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_EX), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + /* CONTROL */ + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_ALL), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_STATE_CHANGE), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_PUT), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_DEMOTE_RQ), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_PROMOTE), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_QUEUE), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_LOCK_TIME), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_PIN), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_LOG_FLUSH), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_LOG_BLOCKS), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_AIL_FLUSH), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_BLOCK_ALLOC), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_BMAP), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_RS), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_BUFFER_SIZE_KB), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOBAL_TRACING), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_WORSTGLOCK), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_LATENCY), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_FTRACE_GLOCK_THRESHOLD), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +int +metrictable_size(void) +{ + return sizeof(metrictable)/sizeof(metrictable[0]); +} + +static dev_t +gfs2_device_identifier(const char *name) +{ + char buffer[4096]; + int major, minor; + dev_t dev_id; + FILE *fp; + + dev_id = makedev(0, 0); /* Error case */ + + /* gfs2_glock_lock_time requires block device id for each filesystem + * in order to match to the lock data, this info can be found in + * /sys/fs/gfs2/NAME/id, we extract the data and store it in gfs2_fs->dev + * + */ + snprintf(buffer, sizeof(buffer), "%s/%s/id", gfs2_sysdir, name); + buffer[sizeof(buffer)-1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL) + return oserror(); + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + sscanf(buffer, "%d:%d", &major, &minor); + dev_id = makedev(major, minor); + } + fclose(fp); + + return dev_id; +} + +/* + * Update the GFS2 filesystems instance domain. This will change + * as filesystems are mounted and unmounted (and re-mounted, etc). + * Using the pmdaCache interfaces simplifies things and provides us + * with guarantees around consistent instance numbering in all of + * those interesting corner cases. + */ +static int +gfs2_instance_refresh(void) +{ + int i, sts, count, gfs2_status; + struct dirent **files; + pmInDom indom = INDOM(GFS_FS_INDOM); + + pmdaCacheOp(indom, PMDA_CACHE_INACTIVE); + + /* update indom cache based on scan of /sys/fs/gfs2 */ + count = scandir(gfs2_sysdir, &files, NULL, NULL); + if (count < 0) { /* give some feedback as to GFS2 kernel state */ + if (oserror() == EPERM) + gfs2_status = PM_ERR_PERMISSION; + else if (oserror() == ENOENT) + gfs2_status = PM_ERR_AGAIN; /* we might see a mount later */ + else + gfs2_status = PM_ERR_APPVERSION; + } else { + gfs2_status = 0; /* we possibly have stats available */ + } + + for (i = 0; i < count; i++) { + struct gfs2_fs *fs; + const char *name = files[i]->d_name; + + if (name[0] == '.') + continue; + + sts = pmdaCacheLookupName(indom, name, NULL, (void **)&fs); + if (sts == PM_ERR_INST || (sts >= 0 && fs == NULL)){ + fs = calloc(1, sizeof(struct gfs2_fs)); + if (fs == NULL) + return PM_ERR_AGAIN; + + fs->dev_id = gfs2_device_identifier(name); + + if ((major(fs->dev_id) == 0) && (minor(fs->dev_id) == 0)) { + free(fs); + return PM_ERR_AGAIN; + } + } + else if (sts < 0) + continue; + + /* (re)activate this entry for the current query */ + pmdaCacheStore(indom, PMDA_CACHE_ADD, name, (void *)fs); + } + + for (i = 0; i < count; i++) + free(files[i]); + if (count > 0) + free(files); + return gfs2_status; +} + +static int +gfs2_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + gfs2_instance_refresh(); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +gfs2_fetch_refresh(pmdaExt *pmda, int *need_refresh) +{ + pmInDom indom = INDOM(GFS_FS_INDOM); + struct gfs2_fs *fs; + char *name; + int i, sts; + + if ((sts = gfs2_instance_refresh()) < 0) + return sts; + + for (pmdaCacheOp(indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + if (!pmdaCacheLookup(indom, i, &name, (void **)&fs) || !fs) + continue; + if (need_refresh[CLUSTER_GLOCKS]) + gfs2_refresh_glocks(gfs2_sysfsdir, name, &fs->glocks); + if (need_refresh[CLUSTER_SBSTATS]) + gfs2_refresh_sbstats(gfs2_sysfsdir, name, &fs->sbstats); + if (need_refresh[CLUSTER_GLSTATS]) + gfs2_refresh_glstats(gfs2_sysfsdir, name, &fs->glstats); + } + + if (need_refresh[CLUSTER_TRACEPOINTS] || need_refresh[CLUSTER_WORSTGLOCK] || need_refresh[CLUSTER_LATENCY]) + gfs2_refresh_ftrace_stats(indom); + + return sts; +} + +static int +gfs2_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i, sts, need_refresh[NUM_CLUSTERS] = { 0 }; + + for (i = 0; i < numpmid; i++) { + __pmID_int *idp = (__pmID_int *)&(pmidlist[i]); + if (idp->cluster < NUM_CLUSTERS) + need_refresh[idp->cluster]++; + } + + if ((sts = gfs2_fetch_refresh(pmda, need_refresh)) < 0) + return sts; + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * callback provided to pmdaFetch + */ +static int +gfs2_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + struct gfs2_fs *fs; + int sts; + + switch (idp->cluster) { + case CLUSTER_GLOCKS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_glocks_fetch(idp->item, &fs->glocks, atom); + + case CLUSTER_SBSTATS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_sbstats_fetch(idp->item, &fs->sbstats, atom); + + case CLUSTER_GLSTATS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_glstats_fetch(idp->item, &fs->glstats, atom); + + case CLUSTER_TRACEPOINTS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_ftrace_fetch(idp->item, &fs->ftrace, atom); + + case CLUSTER_WORSTGLOCK: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void**)&fs); + if (sts < 0) + return sts; + return gfs2_worst_glock_fetch(idp->item, &fs->worst_glock, atom); + + case CLUSTER_LATENCY: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void**)&fs); + if (sts < 0) + return sts; + return gfs2_latency_fetch(idp->item, &fs->latency, atom); + + case CLUSTER_CONTROL: + return gfs2_control_fetch(idp->item, atom); + + default: /* unknown cluster */ + return PM_ERR_PMID; + } + + return 1; +} + +/* + * Enable all tracepoints by default on init + */ +static void +gfs2_tracepoints_init() +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/events/gfs2/enable", "w"); + if (!fp) { + fprintf(stderr, "Unable to automatically enable GFS2 tracepoints"); + } else { + fprintf(fp, "%d\n", 1); + fclose(fp); + } +} + +/* + * Set default trace_pipe buffer size per cpu on init (32MB) + */ +static void +gfs2_buffer_default_size_set() +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/buffer_size_kb", "w"); + if (!fp) { + fprintf(stderr, "Unable to set default buffer size"); + } else { + fprintf(fp, "%d\n", 32768); /* Default 32MB per cpu */ + fclose(fp); + } +} + +/* + * Some version of ftrace are missing the irq-info option which alters the + * trace-pipe output, because of this we check for the option and if exists + * we switch off irq info output in trace_pipe + */ +static void +gfs2_ftrace_irq_info_set() +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/options/irq-info", "w"); + if (fp) { + /* We only need to set value if irq-info exists */ + fprintf(fp, "0"); /* Switch off irq-info in trace_pipe */ + fclose(fp); + } +} + +static int +gfs2_store(pmResult *result, pmdaExt *pmda) +{ + int i; + int sts = 0; + pmValueSet *vsp; + __pmID_int *pmidp; + + for (i = 0; i < result->numpmid && !sts; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item <= CONTROL_BUFFER_SIZE_KB) { + sts = gfs2_control_set_value(control_locations[pmidp->item], vsp); + } + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item == CONTROL_WORSTGLOCK) { + sts = worst_glock_set_state(vsp); + } + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item == CONTROL_LATENCY) { + sts = latency_set_state(vsp); + } + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item == CONTROL_FTRACE_GLOCK_THRESHOLD) { + sts = ftrace_set_threshold(vsp); + } + } + return sts; +} + +static int +gfs2_text(int ident, int type, char **buf, pmdaExt *pmda) +{ + if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) { + int sts = pmdaDynamicLookupText(ident, type, buf, pmda); + if (sts != -ENOENT) + return sts; + } + return pmdaText(ident, type, buf, pmda); +} + +static int +gfs2_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + __pmnsTree *tree = pmdaDynamicLookupName(pmda, name); + return pmdaTreePMID(tree, name, pmid); +} + +static int +gfs2_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + __pmnsTree *tree = pmdaDynamicLookupPMID(pmda, pmid); + return pmdaTreeName(tree, pmid, nameset); +} + +static int +gfs2_children(const char *name, int flag, char ***kids, int **sts, pmdaExt *pmda) +{ + __pmnsTree *tree = pmdaDynamicLookupName(pmda, name); + return pmdaTreeChildren(tree, name, flag, kids, sts); +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +gfs2_init(pmdaInterface *dp) +{ + int nindoms = sizeof(indomtable)/sizeof(indomtable[0]); + int nmetrics = sizeof(metrictable)/sizeof(metrictable[0]); + + if (dp->status != 0) + return; + + dp->version.four.instance = gfs2_instance; + dp->version.four.store = gfs2_store; + dp->version.four.fetch = gfs2_fetch; + dp->version.four.text = gfs2_text; + dp->version.four.pmid = gfs2_pmid; + dp->version.four.name = gfs2_name; + dp->version.four.children = gfs2_children; + pmdaSetFetchCallBack(dp, gfs2_fetchCallBack); + + gfs2_sbstats_init(metrictable, nmetrics); + gfs2_worst_glock_init(metrictable, nmetrics); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, indomtable, nindoms, metrictable, nmetrics); + + /* Set defaults */ + gfs2_tracepoints_init(); /* Enables gfs2 tracepoints */ + gfs2_buffer_default_size_set(); /* Sets default buffer size */ + gfs2_ftrace_irq_info_set(); /* Disables irq-info output with trace_pipe */ +} + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +static pmdaOptions opts = { + .short_options = "D:d:l:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + snprintf(helppath, sizeof(helppath), "%s%c" "gfs2" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, GFS2, "gfs2.log", helppath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + pmdaOpenLog(&dispatch); + gfs2_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/gfs2/pmdagfs2.h b/src/pmdas/gfs2/pmdagfs2.h new file mode 100644 index 0000000..f897d13 --- /dev/null +++ b/src/pmdas/gfs2/pmdagfs2.h @@ -0,0 +1,57 @@ +/* + * Global Filesystem v2 (GFS2) PMDA + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef PMDAGFS2_H +#define PMDAGFS2_H + +#include "glocks.h" +#include "sbstats.h" +#include "glstats.h" +#include "worst_glock.h" +#include "ftrace.h" +#include "latency.h" +#include "control.h" + +enum { + CLUSTER_GLOCKS = 0, /* 0 - /sys/kernel/debug/gfs2//glocks */ + CLUSTER_SBSTATS, /* 1 - /sys/kernel/debug/gfs2//sbstats */ + CLUSTER_GLSTATS, /* 2 - /sys/kernel/debug/gfs2//glstats */ + CLUSTER_TRACEPOINTS, /* 3 - /sys/kernel/debug/tracing/events/gfs2/ */ + CLUSTER_WORSTGLOCK, /* 4 - Custom metric for worst glock */ + CLUSTER_LATENCY, /* 5 - Custom metric for working out latency of filesystem operations */ + CLUSTER_CONTROL, /* 6 - Control for specific tracepoint enabling (for installs without all of the GFS2 tracepoints) */ + NUM_CLUSTERS +}; + +enum { + GFS_FS_INDOM = 0, /* 0 -- mounted gfs filesystem names */ + NUM_INDOMS +}; + +struct gfs2_fs { + dev_t dev_id; + struct glocks glocks; + struct sbstats sbstats; + struct glstats glstats; + struct ftrace ftrace; + struct worst_glock worst_glock; + struct latency latency; +}; + +extern pmdaMetric metrictable[]; +extern int metrictable_size(); + +#endif /*PMDAGFS2_H*/ diff --git a/src/pmdas/gfs2/pmns b/src/pmdas/gfs2/pmns new file mode 100644 index 0000000..7d8a13a --- /dev/null +++ b/src/pmdas/gfs2/pmns @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Changed GFS2:*:* to root number 115 to remove ASCII parse errors during + * install of PMDA whilst testing. + */ +gfs2 { + glocks + sbstats GFS2:*:* + glstats + tracepoints + worst_glock GFS2:*:* + latency + control +} + +gfs2.glocks { + total GFS2:0:0 + shared GFS2:0:1 + unlocked GFS2:0:2 + deferred GFS2:0:3 + exclusive GFS2:0:4 +} + +gfs2.glstats { + total GFS2:2:0 + trans GFS2:2:1 + inode GFS2:2:2 + rgrp GFS2:2:3 + meta GFS2:2:4 + iopen GFS2:2:5 + flock GFS2:2:6 + quota GFS2:2:8 + journal GFS2:2:9 +} + +gfs2.tracepoints { + glock_state_change + glock_put + demote_rq + promote + glock_queue + glock_lock_time + pin + log_flush + log_block + ail_flush + block_alloc + bmap + rs +} + +gfs2.tracepoints.glock_state_change { + total GFS2:3:0 + null_lock GFS2:3:1 + concurrent_read GFS2:3:2 + concurrent_write GFS2:3:3 + protected_read GFS2:3:4 + protected_write GFS2:3:5 + exclusive GFS2:3:6 + glocks +} + +gfs2.tracepoints.glock_state_change.glocks { + changed_target GFS2:3:7 + missed_target GFS2:3:8 +} + +gfs2.tracepoints.glock_put { + total GFS2:3:9 + null_lock GFS2:3:10 + concurrent_read GFS2:3:11 + concurrent_write GFS2:3:12 + protected_read GFS2:3:13 + protected_write GFS2:3:14 + exclusive GFS2:3:15 +} + +gfs2.tracepoints.demote_rq { + total GFS2:3:16 + null_lock GFS2:3:17 + concurrent_read GFS2:3:18 + concurrent_write GFS2:3:19 + protected_read GFS2:3:20 + protected_write GFS2:3:21 + exclusive GFS2:3:22 + requested +} + +gfs2.tracepoints.demote_rq.requested { + remote GFS2:3:23 + local GFS2:3:24 +} + +gfs2.tracepoints.promote { + total GFS2:3:25 + first + other +} + +gfs2.tracepoints.promote.first { + null_lock GFS2:3:26 + concurrent_read GFS2:3:27 + concurrent_write GFS2:3:28 + protected_read GFS2:3:29 + protected_write GFS2:3:30 + exclusive GFS2:3:31 +} + +gfs2.tracepoints.promote.other { + null_lock GFS2:3:32 + concurrent_read GFS2:3:33 + concurrent_write GFS2:3:34 + protected_read GFS2:3:35 + protected_write GFS2:3:36 + exclusive GFS2:3:37 +} + +gfs2.tracepoints.glock_queue { + total GFS2:3:38 + queue + dequeue +} + +gfs2.tracepoints.glock_queue.queue { + total GFS2:3:39 + null_lock GFS2:3:40 + concurrent_read GFS2:3:41 + concurrent_write GFS2:3:42 + protected_read GFS2:3:43 + protected_write GFS2:3:44 + exclusive GFS2:3:45 +} + +gfs2.tracepoints.glock_queue.dequeue { + total GFS2:3:46 + null_lock GFS2:3:47 + concurrent_read GFS2:3:48 + concurrent_write GFS2:3:49 + protected_read GFS2:3:50 + protected_write GFS2:3:51 + exclusive GFS2:3:52 +} + +gfs2.tracepoints.glock_lock_time { + total GFS2:3:53 + trans GFS2:3:54 + inode GFS2:3:55 + rgrp GFS2:3:56 + meta GFS2:3:57 + iopen GFS2:3:58 + flock GFS2:3:59 + quota GFS2:3:60 + journal GFS2:3:61 +} + +gfs2.tracepoints.pin { + total GFS2:3:62 + pin_total GFS2:3:63 + unpin_total GFS2:3:64 + longest_pinned GFS2:3:65 +} + +gfs2.tracepoints.log_flush { + total GFS2:3:66 +} + +gfs2.tracepoints.log_block { + total GFS2:3:67 +} + +gfs2.tracepoints.ail_flush { + total GFS2:3:68 +} + +gfs2.tracepoints.block_alloc { + total GFS2:3:69 + free GFS2:3:70 + used GFS2:3:71 + dinode GFS2:3:72 + unlinked GFS2:3:73 +} + +gfs2.tracepoints.bmap { + total GFS2:3:74 + create GFS2:3:75 + nocreate GFS2:3:76 +} + +gfs2.tracepoints.rs { + total GFS2:3:77 + del GFS2:3:78 + tdel GFS2:3:79 + ins GFS2:3:80 + clm GFS2:3:81 +} + +gfs2.latency { + grant + demote + queue +} + +gfs2.latency.grant { + all GFS2:5:0 + null_lock GFS2:5:1 + concurrent_read GFS2:5:2 + concurrent_write GFS2:5:3 + protected_read GFS2:5:4 + protected_write GFS2:5:5 + exclusive GFS2:5:6 +} + +gfs2.latency.demote { + all GFS2:5:7 + null_lock GFS2:5:8 + concurrent_read GFS2:5:9 + concurrent_write GFS2:5:10 + protected_read GFS2:5:11 + protected_write GFS2:5:12 + exclusive GFS2:5:13 +} + +gfs2.latency.queue { + all GFS2:5:14 + null_lock GFS2:5:15 + concurrent_read GFS2:5:16 + concurrent_write GFS2:5:17 + protected_read GFS2:5:18 + protected_write GFS2:5:19 + exclusive GFS2:5:20 +} + +gfs2.control { + tracepoints + buffer_size_kb GFS2:6:14 + global_tracing GFS2:6:15 + worst_glock GFS2:6:16 + latency GFS2:6:17 + glock_threshold GFS2:6:18 +} + +gfs2.control.tracepoints { + all GFS2:6:0 + glock_state_change GFS2:6:1 + glock_put GFS2:6:2 + demote_rq GFS2:6:3 + promote GFS2:6:4 + glock_queue GFS2:6:5 + glock_lock_time GFS2:6:6 + pin GFS2:6:7 + log_flush GFS2:6:8 + log_blocks GFS2:6:9 + ail_flush GFS2:6:10 + block_alloc GFS2:6:11 + bmap GFS2:6:12 + rs GFS2:6:13 +} diff --git a/src/pmdas/gfs2/root b/src/pmdas/gfs2/root new file mode 100644 index 0000000..5f8b73b --- /dev/null +++ b/src/pmdas/gfs2/root @@ -0,0 +1,16 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { + gfs2 + +} + +#ifndef GFS2 +#define GFS2 115 +#endif + +#include "pmns" diff --git a/src/pmdas/gfs2/sbstats.c b/src/pmdas/gfs2/sbstats.c new file mode 100644 index 0000000..d8d4668 --- /dev/null +++ b/src/pmdas/gfs2/sbstats.c @@ -0,0 +1,264 @@ +/* + * GFS2 sbstats sysfs file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "pmdagfs2.h" + +#include + +static const char *stattype[] = { + [LOCKSTAT_SRTT] = "srtt", + [LOCKSTAT_SRTTVAR] = "srttvar", + [LOCKSTAT_SRTTB] = "srttb", + [LOCKSTAT_SRTTVARB] = "srttvarb", + [LOCKSTAT_SIRT] = "sirt", + [LOCKSTAT_SIRTVAR] = "sirtvar", + [LOCKSTAT_DCOUNT] = "dlm", + [LOCKSTAT_QCOUNT] = "queue", +}; + +static const char *stattext[] = { + [LOCKSTAT_SRTT] = "Non-blocking smoothed round trip time", + [LOCKSTAT_SRTTVAR] = "Non-blocking smoothed variance", + [LOCKSTAT_SRTTB] = "Blocking smoothed round trip time", + [LOCKSTAT_SRTTVARB] = "Blocking smoothed variance", + [LOCKSTAT_SIRT] = "Smoothed inter-request time", + [LOCKSTAT_SIRTVAR] = "Smoothed inter-request variance", + [LOCKSTAT_DCOUNT] = "Count of Distributed Lock Manager requests", + [LOCKSTAT_QCOUNT] = "Count of gfs2_holder queues", +}; + +static const char *locktype[] = { + [LOCKTYPE_RESERVED] = "reserved", + [LOCKTYPE_NONDISK] = "nondisk", + [LOCKTYPE_INODE] = "inode", + [LOCKTYPE_RGRB] = "rgrp", + [LOCKTYPE_META] = "meta", + [LOCKTYPE_IOPEN] = "iopen", + [LOCKTYPE_FLOCK] = "flock", + [LOCKTYPE_PLOCK] = "plock", + [LOCKTYPE_QUOTA] = "quota", + [LOCKTYPE_JOURNAL] = "journal", +}; + +int +gfs2_sbstats_fetch(int item, struct sbstats *fs, pmAtomValue *atom) +{ + /* Check for valid metric count */ + if (item < 0 || item >= SBSTATS_COUNT) + return PM_ERR_PMID; + + /* Check for no values recorded */ + if(fs->values[item] == UINT64_MAX) + return 0; + + atom->ull = fs->values[item]; + return 1; +} + +int +gfs2_refresh_sbstats(const char *sysfs, const char *name, struct sbstats *sb) +{ + unsigned int id = 0; + char buffer[4096]; + FILE *fp; + + /* Reset all counter for this fs */ + memset(sb, 0, sizeof(*sb)); + + snprintf(buffer, sizeof(buffer), "%s/%s/sbstats", sysfs, name); + buffer[sizeof(buffer)-1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL){ + /* + * We set the values to UINT64_MAX to signify we have no + * current values (no metric support or debugfs not mounted) + * + */ + memset(sb, -1, sizeof(*sb)); + return -oserror(); + } + + /* + * Read through sbstats file one line at a time. This is called on each + * and every fetch request, so should be as quick as we can make it. + * + * Once we've skipped over the heading, the format is fixed so to make this + * faster (we expect the kernel has a fixed set of types/stats) we go right + * ahead and index directly into the stats structure for each line. We do + * check validity too - if there's a mismatch, we throw our toys. + * + * Also, we aggregate the per-CPU data for each statistic - we could go for + * separate per-CPU metrics as well, but for now just keep it simple until + * there's a real need for that extra complexity. + * + */ + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + char *typestr, *statstr, *end; + char *p = buffer; + + if (strncmp(p, "type", 4) == 0) + continue; + if (id > SBSTATS_COUNT) + break; + + typestr = p; + for (typestr = p; !isspace((int)*p); p++) { } /* skip lock type */ + for (; isspace((int)*p); p++) { *p = '\0'; } /* eat whitespace */ + for (statstr = p; *p != ':'; p++) { } /* skip stat type */ + *p = '\0'; + + /* verify that the id we are up to matches what we see in the file */ + unsigned int type = id / NUM_LOCKSTATS; + unsigned int stat = id % NUM_LOCKSTATS; + if (strcmp(typestr, locktype[type]) != 0) { + __pmNotifyErr(LOG_ERR, + "unexpected sbstat type \"%s\" (want %s at line %u)", + typestr, locktype[type], id); + break; /* eh? */ + } + if (strcmp(statstr, stattype[stat]) != 0) { + __pmNotifyErr(LOG_ERR, + "unexpected sbstat stat \"%s\" (want %s at line %u)", + statstr, stattype[stat], id); + break; /* wha? */ + } + + /* decode all of the (per-CPU) values until the end of line */ + for (p++; *p != '\0'; p++) { + __uint64_t value; + + value = strtoull(p, &end, 10); + if (end == p) + break; + sb->values[id] += value; + p = end; + } + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_INFO, + "got expected sbstat type \"%s\", stat \"%s\" at line %u", + typestr, statstr, id); + + /* now we can move on to the next metric (on the next line) */ + id++; + } + fclose(fp); + return (id == SBSTATS_COUNT) ? 0 : -EINVAL; +} + +static void +add_pmns_node(__pmnsTree *tree, int domain, int cluster, int lock, int stat) +{ + char entry[64]; + pmID pmid = pmid_build(domain, cluster, (lock * NUM_LOCKSTATS) + stat); + + snprintf(entry, sizeof(entry), + "gfs2.sbstats.%s.%s", locktype[lock], stattype[stat]); + __pmAddPMNSNode(tree, pmid, entry); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 sbstats added %s (%s)", entry, pmIDStr(pmid)); +} + +static int +refresh_sbstats(pmdaExt *pmda, __pmnsTree **tree) +{ + int t, s, sts; + static __pmnsTree *sbstats_tree; + + if (sbstats_tree) { + *tree = sbstats_tree; + } else if ((sts = __pmNewPMNS(&sbstats_tree)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create sbstats names: %s\n", + pmProgname, pmErrStr(sts)); + *tree = NULL; + } else { + for (t = 0; t < NUM_LOCKTYPES; t++) + for (s = 0; s < NUM_LOCKSTATS; s++) + add_pmns_node(sbstats_tree, pmda->e_domain, CLUSTER_SBSTATS, t, s); + *tree = sbstats_tree; + return 1; + } + return 0; +} + +/* + * Create a new metric table entry based on an existing one. + * + */ +static void +refresh_metrictable(pmdaMetric *source, pmdaMetric *dest, int lock) +{ + int item = pmid_item(source->m_desc.pmid); + int domain = pmid_domain(source->m_desc.pmid); + int cluster = pmid_cluster(source->m_desc.pmid); + + memcpy(dest, source, sizeof(pmdaMetric)); + item += lock * NUM_LOCKSTATS; + dest->m_desc.pmid = pmid_build(domain, cluster, item); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 sbstats refresh_metrictable: (%p -> %p) " + "metric ID dup: %d.%d.%d -> %d.%d.%d\n", + source, dest, domain, cluster, + pmid_item(source->m_desc.pmid), domain, cluster, item); +} + +/* + * Used to answer the question: how much extra space needs to be + * allocated in the metric table for (dynamic) sbstats metrics? + * + */ +static void +size_metrictable(int *total, int *trees) +{ + *total = NUM_LOCKSTATS; + *trees = NUM_LOCKTYPES; +} + +static int +sbstats_text(pmdaExt *pmda, pmID pmid, int type, char **buf) +{ + int item = pmid_item(pmid); + static char text[128]; + + if (pmid_cluster(pmid) != CLUSTER_SBSTATS) + return PM_ERR_PMID; + if (item < 0 || item >= SBSTATS_COUNT) + return PM_ERR_PMID; + snprintf(text, sizeof(text), "%s for %s glocks", + stattext[item % NUM_LOCKSTATS], locktype[item / NUM_LOCKSTATS]); + + *buf = text; + + return 0; +} + +void +gfs2_sbstats_init(pmdaMetric *metrics, int nmetrics) +{ + int set[] = { CLUSTER_SBSTATS }; + + pmdaDynamicPMNS("gfs2.sbstats", + set, sizeof(set)/sizeof(int), + refresh_sbstats, sbstats_text, + refresh_metrictable, size_metrictable, + metrics, nmetrics); +} diff --git a/src/pmdas/gfs2/sbstats.h b/src/pmdas/gfs2/sbstats.h new file mode 100644 index 0000000..5e71791 --- /dev/null +++ b/src/pmdas/gfs2/sbstats.h @@ -0,0 +1,56 @@ +/* + * GFS2 sbstats sysfs file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef SBSTATS_H +#define SBSTATS_H + +enum { + LOCKSTAT_SRTT = 0, /* Non-blocking smoothed round trip time */ + LOCKSTAT_SRTTVAR, /* Non-blocking smoothed variance */ + LOCKSTAT_SRTTB, /* Blocking smoothed round trip time */ + LOCKSTAT_SRTTVARB, /* Blocking smoothed variance */ + LOCKSTAT_SIRT, /* Smoothed inter-request time */ + LOCKSTAT_SIRTVAR, /* Smoothed inter-request variance */ + LOCKSTAT_DCOUNT, /* Count of DLM requests */ + LOCKSTAT_QCOUNT, /* Count of gfs2_holder queues */ + NUM_LOCKSTATS +}; + +enum { + LOCKTYPE_RESERVED = 0, + LOCKTYPE_NONDISK, + LOCKTYPE_INODE, + LOCKTYPE_RGRB, + LOCKTYPE_META, + LOCKTYPE_IOPEN, + LOCKTYPE_FLOCK, + LOCKTYPE_PLOCK, + LOCKTYPE_QUOTA, + LOCKTYPE_JOURNAL, + NUM_LOCKTYPES +}; + +#define SBSTATS_COUNT (NUM_LOCKSTATS*NUM_LOCKTYPES) + +struct sbstats { + __uint64_t values[SBSTATS_COUNT]; +}; + +extern void gfs2_sbstats_init(pmdaMetric *, int); +extern int gfs2_sbstats_fetch(int, struct sbstats *, pmAtomValue *); +extern int gfs2_refresh_sbstats(const char *, const char *, struct sbstats *); + +#endif /* SBSTATS_H */ diff --git a/src/pmdas/gfs2/worst_glock.c b/src/pmdas/gfs2/worst_glock.c new file mode 100644 index 0000000..ed851c3 --- /dev/null +++ b/src/pmdas/gfs2/worst_glock.c @@ -0,0 +1,391 @@ +/* + * GFS2 gfs2_glock_lock_time trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "pmdagfs2.h" + +#include "ftrace.h" +#include "worst_glock.h" + +#include +#include +#include +#include + +static struct glock glock_data; +static int reset_flag; + +static int worst_glock_state = DEFAULT_WORST_GLOCK_STATE; + +static const char *stattype[] = { + [WORSTGLOCK_LOCK_TYPE] = "lock_type", + [WORSTGLOCK_NUMBER] = "number", + [WORSTGLOCK_SRTT] = "srtt", + [WORSTGLOCK_SRTTVAR] = "srttvar", + [WORSTGLOCK_SRTTB] = "srttb", + [WORSTGLOCK_SRTTVARB] = "srttvarb", + [WORSTGLOCK_SIRT] = "sirt", + [WORSTGLOCK_SIRTVAR] = "sirtvar", + [WORSTGLOCK_DLM] = "dlm", + [WORSTGLOCK_QUEUE] = "queue", +}; + +static const char *stattext[] = { + [WORSTGLOCK_LOCK_TYPE] = "Glock type number", + [WORSTGLOCK_NUMBER] = "Inode or resource group number", + [WORSTGLOCK_SRTT] = "Non-blocking smoothed round trip time", + [WORSTGLOCK_SRTTVAR] = "Non-blocking smoothed variance", + [WORSTGLOCK_SRTTB] = "Blocking smoothed round trip time", + [WORSTGLOCK_SRTTVARB] = "Blocking smoothed variance", + [WORSTGLOCK_SIRT] = "Smoothed Inter-request time", + [WORSTGLOCK_SIRTVAR] = "Smoothed Inter-request variance", + [WORSTGLOCK_DLM] = "Count of Distributed Lock Manager requests", + [WORSTGLOCK_QUEUE] = "Count of gfs2_holder queues", +}; + +static const char *topnum[] = { + [TOPNUM_FIRST] = "first", + [TOPNUM_SECOND] = "second", + [TOPNUM_THIRD] = "third", + [TOPNUM_FOURTH] = "fourth", + [TOPNUM_FIFTH] = "fifth", + [TOPNUM_SIXTH] = "sixth", + [TOPNUM_SEVENTH] = "seventh", + [TOPNUM_EIGHTH] = "eighth", + [TOPNUM_NINTH] = "ninth", + [TOPNUM_TENTH] = "tenth", +}; + +/* + * Sets the value of worst_glock_state using pmstore, value + * must be 0 or 1. + */ +int +worst_glock_set_state(pmValueSet *vsp) +{ + int value = vsp->vlist[0].value.lval; + + if (value == 0 || value == 1) { + worst_glock_state = value; + + return 0; + } else { + return PM_ERR_SIGN; + } +} + +/* + * Used to see whether the worst_glock metrics are enabled or disabled. Should + * only return either 0 or 1. + */ +int +worst_glock_get_state() +{ + return worst_glock_state; +} + +/* + * Refreshing of the metrics for gfs2.lock_time, some of metrics are of + * a different typing. + */ +int +gfs2_worst_glock_fetch(int item, struct worst_glock *worst_glock, pmAtomValue *atom) +{ + /* If we are assigning, we should set the reset flag for next assign */ + reset_flag = 1; + + int pmid = (item % 10); + int position = (item / 10); + + /* Check if tracepoint is enabled */ + if (worst_glock_get_state() == 0) + return 0; + + /* Check to see if we have values to assign */ + if (worst_glock->glocks[position].lock_type == WORSTGLOCK_INODE || + worst_glock->glocks[position].lock_type == WORSTGLOCK_RGRP){ + switch(pmid){ + case WORSTGLOCK_LOCK_TYPE: + atom->ul = worst_glock->glocks[position].lock_type; /* Glock type number */ + break; + case WORSTGLOCK_NUMBER: + atom->ull = worst_glock->glocks[position].number; /* Inode or resource group number */ + break; + case WORSTGLOCK_SRTT: + atom->ll = worst_glock->glocks[position].srtt; /* Non blocking smoothed round trip time */ + break; + case WORSTGLOCK_SRTTVAR: + atom->ll = worst_glock->glocks[position].srttvar; /* Non blocking smoothed variance */ + break; + case WORSTGLOCK_SRTTB: + atom->ll = worst_glock->glocks[position].srttb; /* Blocking smoothed round trip time */ + break; + case WORSTGLOCK_SRTTVARB: + atom->ll = worst_glock->glocks[position].srttvarb; /* Blocking smoothed variance */ + break; + case WORSTGLOCK_SIRT: + atom->ll = worst_glock->glocks[position].sirt; /* Smoothed Inter-request time */ + break; + case WORSTGLOCK_SIRTVAR: + atom->ll = worst_glock->glocks[position].sirtvar; /* Smoothed Inter-request variance */ + break; + case WORSTGLOCK_DLM: + atom->ll = worst_glock->glocks[position].dlm; /* Count of dlm requests */ + break; + case WORSTGLOCK_QUEUE: + atom->ll = worst_glock->glocks[position].queue; /* Count of gfs2_holder queues */ + break; + default: + return PM_ERR_PMID; + } + return 1; /* Return we have had values */ + } else { + return 0; /* If we have no valid values */ + } +} + +/* + * Comparison function we compare the values; we return the lock which + * is deemed to be the worst. + */ +static int +lock_comparison(const void *a, const void *b) +{ + struct glock *aa = (struct glock *)a; + struct glock *bb = (struct glock *)b; + int true_count = 0; + + /* Case to deal with the empty entries */ + if (aa->lock_type == 0) + return 1; /* Move empty a up the list, b moves down list */ + + if (bb->lock_type == 0) + return -1; /* Move a down the list, empty b moves up list*/ + + /* A sirt (LESS THAN) B sirt = A worse */ + if (aa->sirtvar < bb->sirtvar) + true_count++; + + /* A srtt (MORE THAN) B srtt = A worse */ + if (aa->srttvarb > bb->srttvarb) + true_count++; + + /* A srttb (MORE THAN) B srttb = A worse */ + if (aa->srttvar > bb->srttvar) + true_count++; + + /* If there are more counts where A is worse than B? */ + if ( true_count > 1 ) { + return -1; /* a is worse than b (move a down the list) */ + } else if ( true_count == 1 ){ + /* Tie break condition */ + if ( aa->dlm > bb->queue ) return -1; /* a is worse than b (move a down the list) */ + } + return 1; /* b is worse than a (move b up the list) */ +} + +/* + * We loop through each of our available file-sytems, find the locks that corr- + * esspond to the filesystem. With these locks we find the worst and assign it + * to the filesystem before returning the metric values. + */ +static void +worst_glock_assign_glocks(pmInDom gfs_fs_indom) +{ + int i, j, sts; + struct gfs2_fs *fs; + + /* We walk through for each filesystem */ + for (pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + sts = pmdaCacheLookup(gfs_fs_indom, i, NULL, (void **)&fs); + if (sts != PMDA_CACHE_ACTIVE) + continue; + + /* Clear old entries if reset is set */ + if(reset_flag == 1){ + memset(&fs->worst_glock, 0, sizeof(struct worst_glock)); + reset_flag = 0; + } + + /* Is the entry matching the filesystem we are on? */ + if (fs->dev_id != glock_data.dev_id) + continue; + + /* Check if we are updating an existing entry */ + for (j = 0; j < WORST_GLOCK_TOP; j++) { + if ((fs->worst_glock.glocks[j].lock_type == glock_data.lock_type) && + (fs->worst_glock.glocks[j].number == glock_data.number)) { + fs->worst_glock.glocks[j] = glock_data; + return; + } + } + + /* If not place in next available slot */ + if (fs->worst_glock.assigned_entries < WORST_GLOCK_TOP) { + fs->worst_glock.glocks[fs->worst_glock.assigned_entries] = glock_data; + fs->worst_glock.assigned_entries++; + } else { + fs->worst_glock.glocks[WORST_GLOCK_TOP] = glock_data; /* Place in slot 11 */ + } + qsort(fs->worst_glock.glocks, (WORST_GLOCK_TOP + 1), sizeof(struct glock), lock_comparison); + } +} + +/* + * We work out the individual metric values from our buffer input and store + * them for processing after all of the values have been extracted from the + * trace pipe. + */ +int +gfs2_extract_worst_glock(char **buffer, pmInDom gfs_fs_indom) +{ + struct glock temp; + unsigned int major, minor; + + /* Assign data */ + sscanf(*buffer, + "gfs2_glock_lock_time: %"SCNu32",%"SCNu32" glock %"SCNu32":%"SCNu64" status:%*d flags:%*x tdiff:%*d srtt:%"SCNd64"/%"SCNd64" srttb:%"SCNd64"/%"SCNd64" sirt:%"SCNd64"/%"SCNd64" dcnt:%"SCNd64" qcnt:%"SCNd64, + &major, + &minor, + &temp.lock_type, + &temp.number, + &temp.srtt, + &temp.srttvar, + &temp.srttb, + &temp.srttvarb, + &temp.sirt, + &temp.sirtvar, + &temp.dlm, + &temp.queue + ); + temp.dev_id = makedev(major, minor); + + /* Filter on required lock types */ + if ((temp.lock_type == WORSTGLOCK_INODE || temp.lock_type == WORSTGLOCK_RGRP) && + (temp.dlm > COUNT_THRESHOLD || temp.queue > COUNT_THRESHOLD)) { + + /* Increase counters */ + glock_data = temp; + ftrace_increase_num_accepted_entries(); + } + + worst_glock_assign_glocks(gfs_fs_indom); + return 0; +} + +static void +add_pmns_node(__pmnsTree *tree, int domain, int cluster, int lock, int stat) +{ + char entry[64]; + pmID pmid = pmid_build(domain, cluster, (lock * NUM_GLOCKSTATS) + stat); + + snprintf(entry, sizeof(entry), + "gfs2.worst_glock.%s.%s", topnum[lock], stattype[stat]); + __pmAddPMNSNode(tree, pmid, entry); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 worst_glock added %s (%s)", entry, pmIDStr(pmid)); +} + +static int +refresh_worst_glock(pmdaExt *pmda, __pmnsTree **tree) +{ + int t, s, sts; + static __pmnsTree *worst_glock_tree; + + if (worst_glock_tree) { + *tree = worst_glock_tree; + } else if ((sts = __pmNewPMNS(&worst_glock_tree)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create worst_glock names: %s\n", + pmProgname, pmErrStr(sts)); + *tree = NULL; + } else { + for (t = 0; t < NUM_TOPNUM; t++) + for (s = 0; s < NUM_GLOCKSTATS; s++) + add_pmns_node(worst_glock_tree, pmda->e_domain, CLUSTER_WORSTGLOCK, t, s); + *tree = worst_glock_tree; + return 1; + } + return 0; +} + +/* + * Create a new metric table entry based on an existing one. + * + */ +static void +refresh_metrictable(pmdaMetric *source, pmdaMetric *dest, int lock) +{ + int item = pmid_item(source->m_desc.pmid); + int domain = pmid_domain(source->m_desc.pmid); + int cluster = pmid_cluster(source->m_desc.pmid); + + memcpy(dest, source, sizeof(pmdaMetric)); + item += lock * NUM_GLOCKSTATS; + dest->m_desc.pmid = pmid_build(domain, cluster, item); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 worst_glock refresh_metrictable: (%p -> %p) " + "metric ID dup: %d.%d.%d -> %d.%d.%d\n", + source, dest, domain, cluster, + pmid_item(source->m_desc.pmid), domain, cluster, item); +} + +/* + * Used to answer the question: how much extra space needs to be + * allocated in the metric table for (dynamic) worst_glock metrics? + * + */ +static void +size_metrictable(int *total, int *trees) +{ + *total = NUM_GLOCKSTATS; + *trees = NUM_TOPNUM; +} + +static int +worst_glock_text(pmdaExt *pmda, pmID pmid, int type, char **buf) +{ + int item = pmid_item(pmid); + static char text[128]; + + if (pmid_cluster(pmid) != CLUSTER_WORSTGLOCK) + return PM_ERR_PMID; + if (item < 0 || item >= WORST_GLOCK_COUNT) + return PM_ERR_PMID; + snprintf(text, sizeof(text), "%s for %s worst glock", + stattext[item % NUM_GLOCKSTATS], topnum[item / NUM_TOPNUM]); + + *buf = text; + + return 0; +} + +void +gfs2_worst_glock_init(pmdaMetric *metrics, int nmetrics) +{ + int set[] = { CLUSTER_WORSTGLOCK }; + + pmdaDynamicPMNS("gfs2.worst_glock", + set, sizeof(set)/sizeof(int), + refresh_worst_glock, worst_glock_text, + refresh_metrictable, size_metrictable, + metrics, nmetrics); +} diff --git a/src/pmdas/gfs2/worst_glock.h b/src/pmdas/gfs2/worst_glock.h new file mode 100644 index 0000000..6d1108c --- /dev/null +++ b/src/pmdas/gfs2/worst_glock.h @@ -0,0 +1,91 @@ +/* + * GFS2 gfs2_glock_lock_time trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef LOCK_TIME_H +#define LOCK_TIME_H + +#define COUNT_THRESHOLD 350 +#define DEFAULT_WORST_GLOCK_STATE 1 +#define WORST_GLOCK_TOP 10 +#define WORST_GLOCK_COUNT (NUM_GLOCKSTATS*NUM_TOPNUM) + +enum { + WORSTGLOCK_LOCK_TYPE = 0, + WORSTGLOCK_NUMBER, + WORSTGLOCK_SRTT, + WORSTGLOCK_SRTTVAR, + WORSTGLOCK_SRTTB, + WORSTGLOCK_SRTTVARB, + WORSTGLOCK_SIRT, + WORSTGLOCK_SIRTVAR, + WORSTGLOCK_DLM, + WORSTGLOCK_QUEUE, + NUM_GLOCKSTATS +}; + +enum { + WORSTGLOCK_TRANS = 1, + WORSTGLOCK_INODE = 2, + WORSTGLOCK_RGRP = 3, + WORSTGLOCK_META = 4, + WORSTGLOCK_IOPEN = 5, + WORSTGLOCK_FLOCK = 6, + WORSTGLOCK_RESERVED = 7, + WORSTGLOCK_QUOTA = 8, + WORSTGLOCK_JOURNAL = 9 +}; + +enum { + TOPNUM_FIRST = 0, + TOPNUM_SECOND, + TOPNUM_THIRD, + TOPNUM_FOURTH, + TOPNUM_FIFTH, + TOPNUM_SIXTH, + TOPNUM_SEVENTH, + TOPNUM_EIGHTH, + TOPNUM_NINTH, + TOPNUM_TENTH, + NUM_TOPNUM +}; + +struct glock { + dev_t dev_id; + uint32_t lock_type; /* Glock type number */ + uint64_t number; /* Inode or resource group number */ + int64_t srtt; /* Non blocking smoothed round trip time */ + int64_t srttvar; /* Non blocking smoothed variance */ + int64_t srttb; /* Blocking smoothed round trip time */ + int64_t srttvarb; /* Blocking smoothed variance */ + int64_t sirt; /* Smoothed Inter-request time */ + int64_t sirtvar; /* Smoothed Inter-request variance */ + int64_t dlm; /* Count of dlm requests */ + int64_t queue; /* Count of gfs2_holder queues */ +}; + +struct worst_glock { + struct glock glocks[WORST_GLOCK_TOP + 1]; + int assigned_entries; +}; + +extern void gfs2_worst_glock_init(pmdaMetric *, int); +extern int gfs2_worst_glock_fetch(int, struct worst_glock *, pmAtomValue *); +extern int gfs2_extract_worst_glock(char **, pmInDom); + +extern int worst_glock_get_state(); +extern int worst_glock_set_state(pmValueSet *vsp); + +#endif /* LOCK_TIME_H */ diff --git a/src/pmdas/gluster/GNUmakefile b/src/pmdas/gluster/GNUmakefile new file mode 100644 index 0000000..bb68e16 --- /dev/null +++ b/src/pmdas/gluster/GNUmakefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = gluster +PYSCRIPT = pmda$(IAM).python +LSRCFILES = Install Remove $(PYSCRIPT) + +DOMAIN = GLUSTER +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log + +default_pcp default: check_domain + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(PYSCRIPT) $(PMDADIR)/$(PYSCRIPT) + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PYTHONRULE) diff --git a/src/pmdas/gluster/Install b/src/pmdas/gluster/Install new file mode 100644 index 0000000..c37cf15 --- /dev/null +++ b/src/pmdas/gluster/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the gluster PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gluster +python_opt=true +daemon_opt=false +forced_restart=true + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/gluster/Remove b/src/pmdas/gluster/Remove new file mode 100644 index 0000000..dfc08bb --- /dev/null +++ b/src/pmdas/gluster/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the gluster PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gluster + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/gluster/pmdagluster.python b/src/pmdas/gluster/pmdagluster.python new file mode 100644 index 0000000..de42e0c --- /dev/null +++ b/src/pmdas/gluster/pmdagluster.python @@ -0,0 +1,337 @@ +''' +Performance Metrics Domain Agent exporting Gluster filesystem metrics. +''' +# +# Copyright (c) 2013-2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +import cpmapi as c_api +from pcp.pmapi import pmUnits +from pcp.pmda import PMDA, pmdaMetric, pmdaIndom +from ctypes import c_int, c_long, c_float, POINTER, cast, Structure +import xml.etree.cElementTree as xmltree +from os import getenv +import subprocess + +VOL_INFO_COMMAND = 'gluster --xml volume info' +VOL_STOP_COMMAND = 'gluster --xml volume profile %s stop' +VOL_START_COMMAND = 'gluster --xml volume profile %s start' +VOL_STATS_COMMAND = 'gluster --xml volume profile %s info' + +FILEOPS = [ # append only, do not change the order (changes PMID) + 'ACCESS', 'CREATE', 'DISCARD', 'ENTRYLK', 'FALLOCATE', 'FENTRYLK', + 'FGETXATTR', 'FINODELK', 'FLUSH', 'FREMOVEXATTR', 'FSETATTR', + 'FSETXATTR', 'FSTAT', 'FSYNC', 'FSYNCDIR', 'FTRUNCATE', 'FXATTROP', + 'GETSPEC', 'GETXATTR', 'INODELK', 'LINK', 'LK', 'LOOKUP', 'MKDIR', + 'MKNOD', 'OPEN', 'OPENDIR', 'RCHECKSUM', 'READDIR', 'READDIRP', + 'READLINK', 'READV', 'REMOVEXATTR', 'RENAME', 'RMDIR', 'SETATTR', + 'SETXATTR', 'STAT', 'STATFS', 'SYMLINK', 'TRUNCATE', 'UNLINK', + 'WRITEV', 'XATTROP', 'READ', 'WRITE', 'RELEASE', 'RELEASEDIR', + 'FORGET' +] +FILEOPS_INDICES = {} + +class GlusterVolume(Structure): + ''' Statistic values per-gluster-volume (volume indom cache lookup) ''' + _fields_ = [("distCount", c_int), + ("stripeCount", c_int), + ("replicaCount", c_int), + ("fopHitsEnabled", c_int), + ("latencyEnabled", c_int)] + +class GlusterBrick(Structure): + ''' Statistic values per-gluster-brick, within a volume (brick indom) ''' + _fields_ = [("mintime", c_long * len(FILEOPS)), + ("maxtime", c_long * len(FILEOPS)), + ("avgtime", c_float * len(FILEOPS)), + ("count", c_long * len(FILEOPS)), + ("read_bytes", c_long), + ("write_bytes", c_long)] + +class GlusterPMDA(PMDA): + ''' + Performance Metrics Domain Agent exporting gluster brick metrics. + Install it and make basic use of it if you use glusterfs, as follows: + + # $PCP_PMDAS_DIR/gluster/Install + $ pminfo -fmdtT gluster + ''' + + # volumes and bricks instance domains + volumes = {} + bricks = {} + + def runVolumeInfo(self): + ''' Execute the gluster volume info command to extract volume names ''' + process = subprocess.Popen(self.vol_info, shell = True, + stdout = subprocess.PIPE, stderr = subprocess.PIPE) + out, err = process.communicate() + # self.log('Info command %s output: %s' % (self.vol_info, out)) + return xmltree.fromstring(out) + + def runVolumeProfileInfo(self, volume): + ''' Execute gluster volume profile info command for a given volume ''' + command = self.vol_stats % volume + process = subprocess.Popen(command, shell = True, + stdout = subprocess.PIPE, stderr = subprocess.PIPE) + out, err = process.communicate() + # self.log('Profile command %s output: %s' % (command, out)) + return xmltree.fromstring(out) + + def parseVolumeInfo(self, xml): + ''' Extract the set of volume names from given gluster XML string ''' + volumenames = [] + volsxml = xml.find('volInfo/volumes') + if volsxml == None: + return volumenames + for volxml in volsxml.findall('volume'): + volname = volxml.find('name').text + volumenames.append(volname) + volume = GlusterVolume() + volume.distCount = int(volxml.find('distCount').text) + volume.stripeCount = int(volxml.find('stripeCount').text) + volume.replicaCount = int(volxml.find('replicaCount').text) + volume.fopHitsEnabled = 0 + volume.latencyEnabled = 0 + for option in volxml.findall('options/option'): + name = option.find('name').text + value = option.find('value').text + if (name == 'diagnostics.count-fop-hits' and value == 'on'): + volume.fopHitsEnabled = 1 + if (name == 'diagnostics.latency-measurement' and value == 'on'): + volume.latencyEnabled = 1 + self.volumes[volname] = volume # prepare the volume indom cache + return volumenames + + def parseVolumeProfileInfo(self, volume, xml): + ''' Extract the metric values from a given gluster profile string ''' + for brickxml in xml.findall('volProfile/brick'): + brickname = brickxml.find('brickName').text + brick = GlusterBrick() + for fileop in brickxml.findall('cumulativeStats/fopStats/fop'): + name = fileop.find('name').text + try: + fop = FILEOPS_INDICES[name] + except KeyError: + # self.log('Unrecognised fileops key %s' % name) + pass + else: + brick.count[fop] = long(fileop.find('hits').text) + brick.avgtime[fop] = float(fileop.find('avgLatency').text) + brick.mintime[fop] = long(float(fileop.find('minLatency').text)) + brick.maxtime[fop] = long(float(fileop.find('maxLatency').text)) + brick.read_bytes = long(brickxml.find('cumulativeStats/totalRead').text) + brick.write_bytes = long(brickxml.find('cumulativeStats/totalWrite').text) + self.bricks[brickname] = brick # prepare the bricks indom cache + + + def gluster_refresh(self): + ''' Refresh the values and instances for gluster volumes and bricks ''' + xml = self.runVolumeInfo() + if (xml != None): + for vol in self.parseVolumeInfo(xml): + xml = self.runVolumeProfileInfo(vol) + if (xml != None): + self.parseVolumeProfileInfo(vol, xml) + + def gluster_instance(self, serial): + ''' Called once per "instance request" PDU ''' + if (serial == 0 or serial == 1): + self.gluster_fetch() + + def gluster_fetch(self): + ''' Called once per "fetch" PDU ''' + self.bricks.clear() + self.volumes.clear() + self.gluster_refresh() + self.replace_indom(self.brick_indom, self.bricks) + self.replace_indom(self.volume_indom, self.volumes) + + + def gluster_fetch_thruput_callback(self, item, inst): + ''' + Returns a list of value,status (single pair) for thruput cluster + Helper for the fetch callback + ''' + if (item < 0 or item > 2): + return [c_api.PM_ERR_PMID, 0] + voidp = self.inst_lookup(self.brick_indom, inst) + if (voidp == None): + return [c_api.PM_ERR_INST, 0] + cache = cast(voidp, POINTER(GlusterBrick)) + brick = cache.contents + if (item == 0): + return [brick.read_bytes, 1] + return [brick.write_bytes, 1] + + def gluster_fetch_latency_callback(self, item, inst): + ''' + Returns a list of value,status (single pair) for latency cluster + Helper for the fetch callback + ''' + if (item < 0 or item > len(FILEOPS) * 4): + return [c_api.PM_ERR_PMID, 0] + voidp = self.inst_lookup(self.brick_indom, inst) + if (voidp == None): + return [c_api.PM_ERR_INST, 0] + cache = cast(voidp, POINTER(GlusterBrick)) + brick = cache.contents + fileop = item / 4 + index = item % 4 + if (index == 0): + return [brick.mintime[fileop], 1] + elif (index == 1): + return [brick.maxtime[fileop], 1] + elif (index == 2): + return [brick.avgtime[fileop], 1] + return [brick.count[fileop], 1] + + def gluster_fetch_volumes_callback(self, item, inst): + ''' + Returns a list of value,status (single pair) for volumes cluster + Helper for the fetch callback + ''' + if (item < 0 or item > 3): + return [c_api.PM_ERR_PMID, 0] + voidp = self.inst_lookup(self.volume_indom, inst) + if (voidp == None): + return [c_api.PM_ERR_INST, 0] + cached = cast(voidp, POINTER(GlusterVolume)) + volume = cached.contents + if (item == 0): + enabled = volume.fopHitsEnabled + if (enabled): + enabled = volume.latencyEnabled + return [enabled, 1] + elif (item == 1): + return [volume.distCount, 1] + elif (item == 2): + return [volume.stripeCount, 1] + elif (item == 3): + return [volume.replicaCount, 1] + return [c_api.PM_ERR_PMID, 0] + + def gluster_fetch_callback(self, cluster, item, inst): + ''' + Main fetch callback, defers to helpers for each cluster. + Returns a list of value,status (single pair) for requested pmid/inst + ''' + # self.log("fetch callback for %d.%d[%d]" % (cluster, item, inst)) + if (cluster == 0): + return self.gluster_fetch_thruput_callback(item, inst) + elif (cluster == 1): + return self.gluster_fetch_latency_callback(item, inst) + elif (cluster == 2): + return self.gluster_fetch_volumes_callback(item, inst) + return [c_api.PM_ERR_PMID, 0] + + + def gluster_store_volume_callback(self, inst, val): + ''' Helper for the store callback, volume profile enabling/disabling ''' + sts = 0 + name = self.inst_name_lookup(self.volume_indom, inst) + if (name == None): + sts = c_api.PM_ERR_INST + elif (val < 0): + sts = c_api.PM_ERR_SIGN + elif (val == 0): + command = self.vol_stop % name + # self.log("Running disable command: %s" % command) + subprocess.call(command, shell =True, + stdout = subprocess.PIPE, stderr = subprocess.PIPE) + else: + command = self.vol_start % name + # self.log("Running enable command: %s" % command) + subprocess.call(command, shell =True, + stdout = subprocess.PIPE, stderr = subprocess.PIPE) + return sts + + def gluster_store_callback(self, cluster, item, inst, val): + ''' + Store callback, executed when a request to write to a metric happens + Defers to helpers for each storable metric. Returns a single value. + ''' + # self.log("store callback for %d.%d[%d]" % (cluster, item, inst)) + if (cluster == 2 and item == 0): + return self.gluster_store_volume_callback(inst, val) + elif (cluster == 0 or cluster == 1): + return c_api.PM_ERR_PERMISSION + return c_api.PM_ERR_PMID + + + def __init__(self, name, domain): + PMDA.__init__(self, name, domain) + + self.vol_info = getenv('GLUSTER_VOL_INFO', VOL_INFO_COMMAND) + self.vol_stop = getenv('GLUSTER_VOL_STOP', VOL_STOP_COMMAND) + self.vol_start = getenv('GLUSTER_VOL_START', VOL_START_COMMAND) + self.vol_stats = getenv('GLUSTER_VOL_STATS', VOL_STATS_COMMAND) + + self.volume_indom = self.indom(0) + self.add_indom(pmdaIndom(self.volume_indom, self.volumes)) + + self.brick_indom = self.indom(1) + self.add_indom(pmdaIndom(self.brick_indom, self.bricks)) + + self.add_metric(name + '.brick.read_bytes', pmdaMetric(self.pmid(0, 0), + c_api.PM_TYPE_U64, self.brick_indom, c_api.PM_SEM_COUNTER, + pmUnits(1, 0, 0, c_api.PM_SPACE_BYTE, 0, 0))) + self.add_metric(name + '.brick.write_bytes', pmdaMetric(self.pmid(0, 1), + c_api.PM_TYPE_U64, self.brick_indom, c_api.PM_SEM_COUNTER, + pmUnits(1, 0, 0, c_api.PM_SPACE_BYTE, 0, 0))) + + item = 0 + for fileop in FILEOPS: + metricname = name + '.brick.latency.' + fileop.lower() + '.' + self.add_metric(metricname + 'min', pmdaMetric(self.pmid(1, item), + c_api.PM_TYPE_U64, self.brick_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 1, 0, 0, c_api.PM_TIME_USEC, 0))) + item += 1 + self.add_metric(metricname + 'max', pmdaMetric(self.pmid(1, item), + c_api.PM_TYPE_U64, self.brick_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 1, 0, 0, c_api.PM_TIME_USEC, 0))) + item += 1 + self.add_metric(metricname + 'avg', pmdaMetric(self.pmid(1, item), + c_api.PM_TYPE_FLOAT, self.brick_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 1, 0, 0, c_api.PM_TIME_USEC, 0))) + item += 1 + self.add_metric(metricname + 'count', pmdaMetric(self.pmid(1, item), + c_api.PM_TYPE_U64, self.brick_indom, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE))) + item += 1 + + metricname = name + '.volume.' + self.add_metric(metricname + 'profile', pmdaMetric(self.pmid(2, 0), + c_api.PM_TYPE_32, self.volume_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + self.add_metric(metricname + 'dist.count', pmdaMetric(self.pmid(2, 1), + c_api.PM_TYPE_32, self.volume_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + self.add_metric(metricname + 'stripe.count', pmdaMetric(self.pmid(2, 2), + c_api.PM_TYPE_32, self.volume_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + self.add_metric(metricname + 'replica.count', pmdaMetric(self.pmid(2, 3), + c_api.PM_TYPE_32, self.volume_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + + self.set_fetch(self.gluster_fetch) + self.set_instance(self.gluster_instance) + self.set_fetch_callback(self.gluster_fetch_callback) + self.set_store_callback(self.gluster_store_callback) + + +if __name__ == '__main__': + for index in range(0, len(FILEOPS)): + fileop = FILEOPS[index] + FILEOPS_INDICES[fileop] = index + GlusterPMDA('gluster', 118).run() diff --git a/src/pmdas/gpsd/GNUmakefile b/src/pmdas/gpsd/GNUmakefile new file mode 100644 index 0000000..acd1e76 --- /dev/null +++ b/src/pmdas/gpsd/GNUmakefile @@ -0,0 +1,48 @@ +#!gmake +# +# Copyright (c) 2010 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = gpsd +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/gpsd/Install b/src/pmdas/gpsd/Install new file mode 100644 index 0000000..6b39be4 --- /dev/null +++ b/src/pmdas/gpsd/Install @@ -0,0 +1,34 @@ +#! /bin/sh +# +# Copyright (c) 2010 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the gpsd PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gpsd +perl_opt=true +daemon_opt=false +forced_restart=true + +perl -e "use JSON" 2>/dev/null +if test $? -ne 0; then + echo "JSON perl module is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/gpsd/Remove b/src/pmdas/gpsd/Remove new file mode 100644 index 0000000..2b52da3 --- /dev/null +++ b/src/pmdas/gpsd/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2010 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the gpsd PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gpsd + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/gpsd/pmdagpsd.pl b/src/pmdas/gpsd/pmdagpsd.pl new file mode 100644 index 0000000..c924d04 --- /dev/null +++ b/src/pmdas/gpsd/pmdagpsd.pl @@ -0,0 +1,310 @@ +# +# Copyright (c) 2010 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use JSON; +use Time::HiRes qw ( time ); + +use vars qw( $pmda ); +use vars qw( %devdata %satdata ); + +my $gpspipe = "gpspipe -w"; + +my $json; + +my $gpsd_release = "?"; +my $gpsd_rev = "?"; +my $gpsd_proto_maj = -1; +my $gpsd_proto_min = -1; + +# FIXME: the following will need changing to support multiple devices +my $gpsd_device = undef; +my %devdata = ( + "time" => -1, + "ept" => -1, + "lat" => -1, + "lon" => -1, + "alt" => -1, + "track" => -1, + "speed" => -1, + "climb" => -1, + "mode" => 0, # 0 = no fix + "xdop" => -1, + "ydop" => -1, + "vdop" => -1, + "tdop" => -1, + "hdop" => -1, + "gdop" => -1, + "pdop" => -1, + "num_satellites" => 0, +); +my $gpsd_sat_indom = 0; +my @gpsd_sat_dom = (); + +sub gpsd_pipe_callback +{ + (undef, $_) = @_; + + # $pmda->log("pipe got: $_"); + + my $jtxt = $json->decode($_); + + if ($jtxt->{"class"} eq "VERSION") { + $gpsd_release = $jtxt->{"release"}; + $gpsd_rev = $jtxt->{"rev"}; + $gpsd_proto_maj = $jtxt->{"proto_major"}; + $gpsd_proto_min = $jtxt->{"proto_minor"}; + } elsif ($jtxt->{"class"} eq "DEVICES") { + foreach my $dev (@{$jtxt->{"devices"}}) { + if ($dev->{"class"} eq "DEVICE") { + $pmda->log("gpsd_pipe_callback: oops! multiple " . + "devices detected, only using the first " . + "($gpsd_device)") if (defined($gpsd_device) and + $dev->{"path"} ne $gpsd_device); + + $gpsd_device = $dev->{"path"}; + } else { + $pmda->log("gpsd_pipe_callback: unknown class '" . + $dev->{"class"} . "'"); + } + } + } elsif ($jtxt->{"class"} eq "DEVICE") { + # nothing to do + } elsif ($jtxt->{"class"} eq "WATCH") { + # nothing to do + } elsif ($jtxt->{"class"} eq "TPV") { + return if ($jtxt->{"device"} ne $gpsd_device); + + $devdata{"time"} = $jtxt->{"time"}; + $devdata{"ept"} = $jtxt->{"ept"}; + $devdata{"lat"} = $jtxt->{"lat"}; + $devdata{"lon"} = $jtxt->{"lon"}; + $devdata{"alt"} = $jtxt->{"alt"}; + $devdata{"track"} = $jtxt->{"track"}; + $devdata{"speed"} = $jtxt->{"speed"}; + $devdata{"climb"} = $jtxt->{"climb"}; + $devdata{"mode"} = $jtxt->{"mode"}; + } elsif ($jtxt->{"class"} eq "SKY") { + return if ($jtxt->{"device"} ne $gpsd_device); + + $devdata{"xdop"} = $jtxt->{"xdop"}; + $devdata{"ydop"} = $jtxt->{"ydop"}; + $devdata{"vdop"} = $jtxt->{"vdop"}; + $devdata{"tdop"} = $jtxt->{"tdop"}; + $devdata{"hdop"} = $jtxt->{"hdop"}; + $devdata{"gdop"} = $jtxt->{"gdop"}; + $devdata{"pdop"} = $jtxt->{"pdop"}; + + my %sats = {}; + my @dom = (); + foreach my $sat (@{$jtxt->{"satellites"}}) { + push(@dom, $sat->{"PRN"} => "$sat->{'PRN'}"); + + $sats{"el"}{$sat->{"PRN"}} = $sat->{"el"}; + $sats{"az"}{$sat->{"PRN"}} = $sat->{"az"}; + $sats{"ss"}{$sat->{"PRN"}} = $sat->{"ss"}; + $sats{"used"}{$sat->{"PRN"}} = $sat->{"used"}; + } + %satdata = %sats; + $pmda->replace_indom($gpsd_sat_indom, \@dom); + + $devdata{"num_satellites"} = scalar(@dom) / 2; + } else { + $pmda->log("gpsd_pipe_callback: unknown class '" . $jtxt->{"class"} . "'"); + } +} + +sub gpsd_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + + # $pmda->log("gpsd_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + return (PM_ERR_PMID, 0) if (!defined($metric_name)); + + if ($cluster == 0) { + return (PM_ERR_INST, 0) if ($inst != PM_IN_NULL); + + if ($metric_name eq "gpsd.release") { + return ($gpsd_release, 1); + } elsif ($metric_name eq "gpsd.rev") { + return ($gpsd_rev, 1); + } elsif ($metric_name eq "gpsd.proto.major") { + return ($gpsd_proto_maj, 1); + } elsif ($metric_name eq "gpsd.proto.minor") { + return ($gpsd_proto_min, 1); + } + } + + $metric_name =~ s/^gpsd\.devices\.dev0\.//; + + if ($metric_name =~ m/^satellites\./) { + # satellite info + $metric_name =~ s/^satellites\.//; + + # $pmda->log("gpsd_fetch_callbac2 $metric_name $cluster:$item ($inst): ${satdata{$metric_name}}\n"); + # $pmda->log("gpsd_fetch_callbac2 $metric_name $cluster:$item ($inst): ${satdata{$metric_name}{$inst}}\n"); + return (PM_ERR_INST, 0) if ($inst == PM_IN_NULL); + return (PM_ERR_PMID, 0) if (!defined($satdata{$metric_name}{$inst})); + return ($satdata{$metric_name}{$inst}, 1); + } + + return (PM_ERR_INST, 0) if ($inst != PM_IN_NULL); + return (PM_ERR_PMID, 0) if (!defined($devdata{$metric_name})); + return ($devdata{$metric_name}, 1); +} + +$json = new JSON; + +$pmda = PCP::PMDA->new('gpsd', 105); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.release", '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.rev", '', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.proto.major", '', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.proto.minor", '', ''); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.time", '', ''); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.ept", '', ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.lat", '', ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.lon", '', ''); +$pmda->add_metric(pmda_pmid(1,4), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.alt", '', ''); +$pmda->add_metric(pmda_pmid(1,5), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.track", '', ''); +$pmda->add_metric(pmda_pmid(1,6), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.speed", '', ''); +$pmda->add_metric(pmda_pmid(1,7), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.climb", '', ''); +$pmda->add_metric(pmda_pmid(1,8), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.mode", '', ''); + +$pmda->add_metric(pmda_pmid(1,9), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.xdop", '', ''); +$pmda->add_metric(pmda_pmid(1,10), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.ydop", '', ''); +$pmda->add_metric(pmda_pmid(1,11), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.vdop", '', ''); +$pmda->add_metric(pmda_pmid(1,12), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.tdop", '', ''); +$pmda->add_metric(pmda_pmid(1,13), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.hdop", '', ''); +$pmda->add_metric(pmda_pmid(1,14), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.gdop", '', ''); +$pmda->add_metric(pmda_pmid(1,15), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.pdop", '', ''); + +$pmda->add_metric(pmda_pmid(1,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.num_satellites", '', ''); + +$pmda->add_metric(pmda_pmid(1,100), PM_TYPE_U32, $gpsd_sat_indom, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.satellites.el", '', ''); +$pmda->add_metric(pmda_pmid(1,101), PM_TYPE_U32, $gpsd_sat_indom, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.satellites.az", '', ''); +$pmda->add_metric(pmda_pmid(1,102), PM_TYPE_U32, $gpsd_sat_indom, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.satellites.ss", '', ''); +$pmda->add_metric(pmda_pmid(1,103), PM_TYPE_U32, $gpsd_sat_indom, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "gpsd.devices.dev0.satellites.used", '', ''); + +$pmda->add_indom($gpsd_sat_indom, \@gpsd_sat_dom, '', ''); +$pmda->add_pipe($gpspipe, \&gpsd_pipe_callback, 0); + +$pmda->set_fetch_callback(\&gpsd_fetch_callback); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdagpsd - gpsd performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +values from the gpsd daemon. + +=head1 INSTALLATION + +If you want access to the names and values for gpsd, do the following as +root: + + # cd $PCP_PMDAS_DIR/gpsd + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/gpsd + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/gpsd/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/gpsd/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/gpsd.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/hotproc/GNUakefile b/src/pmdas/hotproc/GNUakefile new file mode 100644 index 0000000..7ebb155 --- /dev/null +++ b/src/pmdas/hotproc/GNUakefile @@ -0,0 +1,52 @@ +#!make + +LTARGETS = help.dir +LLDIRT = domain.h *.log *.dir *.pag pmns help + +PCP_SRC_DEPTH = ../.. +include $(PCP_SRC_DEPTH)/include/commondefs +include $(PCP_SRC_DEPTH)/include/isacommondefs + +PROC_DIR = ../proc +IAM = hotproc +DOMAIN = HOTPROC +IDBTAG = PMDA_$(DOMAIN) +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +install: default + $(INSTALL) -F /usr/pcp/lib -idb "$(IDBTAG)" -lns ../../..$(PMDADIR)/pmda$(IAM) pmda$(IAM) + $(INSTALL) -idb '$(IDBTAG) removeop("rm -f $$rbase$(PMDADIR)/help.*")' -m 755 -dir $(PMDADIR) + $(INSTALL) -F /usr/pcp/pmdas -idb "$(IDBTAG)" -lns ../../..$(PMDADIR) $(IAM) +#if $(BEFORE_IRIX6_5) + $(I_32) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE32) $(STRIPBIN)" -m 555 -src 32/pmda$(IAM) pmda$(IAM) + $(I_64) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE64) $(STRIPBIN)" -m 555 -src 64/pmda$(IAM) pmda$(IAM) +#else + $(I_N32) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE32) $(STRIPBIN)" -m 555 -src N32/pmda$(IAM) pmda$(IAM) + $(I_64) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE64) $(STRIPBIN)" -m 555 -src 64/pmda$(IAM) pmda$(IAM) +#endif + $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG)" -m 555 Install Remove + $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG)" -m 444 README root help pmns domain.h sample.conf general.conf general.pmie + $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG)" -m 444 -src Makefile.install Makefile + +help: $(PROC_DIR)/help help.hotproc pmns + sed < $(PROC_DIR)/help -e 's/proc\./hotproc./g' \ + -e 's/number of processes/number of "interesting" processes/g' \ + | cat - help.hotproc | ./help.fmt > $@ + +help.dir: domain.h help root pmns ../../buildtools/newhelp + PCP_SRC_DEPTH=$(PCP_SRC_DEPTH) $(PCP_SRC_DEPTH)/buildtools/check_help_src help root + +pmns: $(PROC_DIR)/root_proc pmns.hotproc fixpmns.awk + nawk < $(PROC_DIR)/root_proc -f fixpmns.awk \ + | sed -e '/[ ]PROC:/s/PROC:/HOTPROC:/g' -e 's/^proc/hotproc/g' \ + | cat - pmns.hotproc >$@ + +.NOTPARALLEL: +.ORDER: domain.h $(OBJECTS) + +domain.h: ../../pmns/stdpmid + rm -f domain.h + echo "/*" >domain.h + echo " * built from $(PCP_VAR_DIR)/pmns/stdpmid" >>domain.h + echo " */" >>domain.h + nawk <../../pmns/stdpmid >>domain.h '/#define[ ][ ]*$(DOMAIN)[ ]/ { print "#define $(DOMAIN) " $$3 }' diff --git a/src/pmdas/hotproc/GNUmakefile b/src/pmdas/hotproc/GNUmakefile new file mode 100644 index 0000000..2c69ac6 --- /dev/null +++ b/src/pmdas/hotproc/GNUmakefile @@ -0,0 +1,157 @@ +# +# Copyright (c) 1995-2002,2007 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +# This is just to get the source exported to the community. +# 'Makefile' in particular is included only for clues it might offer, +# and should almost certainly be removed from SRCFILES once +# the pmda is working. +LSRCFILES = fixpmns.awk general.pmie help.fmt Install \ + pmns.hotproc Remove sample.conf \ + general.conf help.hotproc Makefile README root +SUBDIRS = src + +default default_pcp: +install install_pcp: +include $(BUILDRULES) + +# Remove # from start of all following lines to get pre-existing +# (but currently non-functional) content + +#IAM = hotproc +#DOMAIN = HOTPROC +# +#TARGETS = $(IAM) pmns help +# +#PROC_DIR = ../linux +#PROC_SRCDIR = $(PROC_DIR)/src +# +#FROM_PROC_H = cluster.h pracinfo.h proc_aux.h pscred.h \ +# pstatus.h pmemory.h proc.h procmem.h psinfo.h \ +# psusage.h ctltab.h nameinfo.h +#FROM_PROC_C = pmemory.c pracinfo.c proc_aux.c \ +# pscred.c psinfo.c pstatus.c psusage.c \ +# ttyname.c procmem.c nameinfo.c +# +#FROM_PROC = $(FROM_PROC_C) $(FROM_PROC_H) +# +#CFILES = $(FROM_PROC_C) \ +# pglobal.c ctltab.c hotproc.c pcpu.c \ +# config.c gram_node.c error.c ppred_values.c +# +#OBJECTS = gram.o lex.o $(CFILES:S/.c/.o/g) +# +#LCOPTS = -fullwarn +#LCINCS = $(PCP_INC_PATH) +#LCDEFS = $(DEBUG) +#LLDOPTS = $(PCP_LIBS_PATH) +#LLDLIBS = $(PCP_PMDALIB) +# +#PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +#LDIRT = domain.h *.log *.dir *.pag +# +#default: $(TARGETS) check_help_src +# +#include $(TOPDIR)/src/include/buildrules.pro +# +#install: default +# $(INSTALL) -m 755 -d $(PMDADIR) +# $(INSTALL) -lns ../../..$(PMDADIR)/pmda$(IAM) pmda$(IAM) $(PCP_SHARE_DIR)/lib +# $(INSTALL) -lns ../../..$(PMDADIR) $(IAM) $(PCP_PMDAS_DIR) +# $(INSTALL) -m 755 pmda$(IAM) $(PMDADIR) +# $(INSTALL) -m 755 Install Remove $(PMDADIR) +# $(INSTALL) -m 644 README root help pmns domain.h sample.conf general.conf general.pmie $(PMDADIR) +# +#$(IAM): $(OBJECTS) +# +#domain.h: ../../pmns/stdpmid +# $(DOMAIN_MAKERULE) +# +#help: $(PROC_DIR)/help help.hotproc pmns +# $(AWK) <$(PROC_DIR)/help '\ +#$1 == "@" { want=0 }\ +#$1 == "@" && $2 ~ /^proc/ { want=1 }\ +#want == 1 { print }' \ +# | sed -e 's/proc\./hotproc./g' \ +# -e 's/number of processes/number of "interesting" processes/g' \ +# | cat - help.hotproc \ +# | ./help.fmt >$@ +# +#pmns: $(PROC_DIR)/root_linux pmns.hotproc fixpmns.awk +# $(AWK) < $(PROC_DIR)/root_linux -f fixpmns.awk \ +# | sed -e '/nprocs/d' -e 's/60:/HOTPROC:/g' -e 's/^proc/hotproc/g' \ +# | cat - pmns.hotproc >$@ +# +#hotproc.o: domain.h +# +#config.o: gram.tab.h +# +#check_help_src: domain.h help root pmns +# PCP_SRC_DEPTH=$(PCP_SRC_DEPTH) $(PCP_SRC_DEPTH)/buildtools/check_help_src help root +# +## +## PROC_SRCDIR dependencies +## +#cluster.h: $(PROC_SRCDIR)/cluster.h +# ln -s $? $@ +#pracinfo.h: $(PROC_SRCDIR)/pracinfo.h +# ln -s $? $@ +#pracinfo.c: $(PROC_SRCDIR)/pracinfo.c +# ln -s $? $@ +#proc_aux.h: $(PROC_SRCDIR)/proc_aux.h +# ln -s $? $@ +#proc_aux.c: $(PROC_SRCDIR)/proc_aux.c +# ln -s $? $@ +#pscred.h: $(PROC_SRCDIR)/pscred.h +# ln -s $? $@ +#pscred.c: $(PROC_SRCDIR)/pscred.c +# ln -s $? $@ +#pstatus.h: $(PROC_SRCDIR)/pstatus.h +# ln -s $? $@ +#pstatus.c: $(PROC_SRCDIR)/pstatus.c +# ln -s $? $@ +#pmemory.h: $(PROC_SRCDIR)/pmemory.h +# ln -s $? $@ +#pmemory.c: $(PROC_SRCDIR)/pmemory.c +# ln -s $? $@ +#proc.h: $(PROC_SRCDIR)/proc.h +# ln -s $? $@ +#procmem.h: $(PROC_SRCDIR)/procmem.h +# ln -s $? $@ +#procmem.c: $(PROC_SRCDIR)/procmem.c +# ln -s $? $@ +#psinfo.h: $(PROC_SRCDIR)/psinfo.h +# ln -s $? $@ +#psinfo.c: $(PROC_SRCDIR)/psinfo.c +# ln -s $? $@ +#psusage.h: $(PROC_SRCDIR)/psusage.h +# ln -s $? $@ +#psusage.c: $(PROC_SRCDIR)/psusage.c +# ln -s $? $@ +#ttyname.c: $(PROC_SRCDIR)/ttyname.c +# ln -s $? $@ +#ctltab.h: $(PROC_SRCDIR)/ctltab.h +# ln -s $? $@ +#nameinfo.h: $(PROC_SRCDIR)/nameinfo.h +# ln -s $? $@ +#nameinfo.c: $(PROC_SRCDIR)/nameinfo.c +# ln -s $? $@ +# +# +# +# +#default_pro : default +# diff --git a/src/pmdas/hotproc/Install b/src/pmdas/hotproc/Install new file mode 100644 index 0000000..e1cc8b1 --- /dev/null +++ b/src/pmdas/hotproc/Install @@ -0,0 +1,150 @@ +#!/bin/sh +# +# Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the hotproc PMDA and/or PMNS +# +# XXX these values are overwritten by pmdaproc.sh! +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +status=1 # failure is the default! +trap "cd /; rm -rf $tmp; exit \$status" 0 1 2 3 15 + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=hotproc +pmda_interface=2 +forced_restart=false + +pmdaSetup + +daemon_opt=true # can install as daemon +dso_opt=false +pipe_opt=true # supports pipe IPC +socket_opt=false # no socket IPC + +# be careful that mortals cannot write any configuration files, as +# these would present a security problem +# +umask 022 + +# PMDA variables +# +configfile="" +refresh=60 +debug=0 +storable="n" + +do_debug=false + +_parsedefaults() +{ + echo "Extracting options from current installation ..." + while getopts D:d:h:i:l:p:st:u: c + do + case $c in + \?) echo "Warning: Unrecognized option in $PCP_PMCDCONF_PATH" + echo " Remove line for the $iam PMDA in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2;; + D ) debug=$OPTARG;; + t ) refresh=$OPTARG;; + s ) storable="n";; + * ) ;; + esac + done + eval configfile='$'$OPTIND +} + +if $do_pmda +then + + # set options from $PCP_PMCDCONF_PATH, if possible + # + ans=`$PCP_AWK_PROG <$PCP_PMCDCONF_PATH ' +$1 == "'$iam'" { printf "%s",$6 + for (i=7;i<=NF;i++) printf " %s",$i + print "" + }'` + if [ ! -z "$ans" ] + then + _parsedefaults $ans + fi + + # go figure out which configuration file to use ... + # + default_configfile=./sample.conf + pmdaChooseConfigFile + if [ -z "$configfile" ] + then + echo "" + echo "Error: Abandoning installation as no configuration file was specified." + exit 1 + fi + + # make sure that the chosen configuration file parses ok + # + $pmda_dir/$pmda_name -C $configfile >$tmp/err 2>&1 + if [ $? -eq 1 ] + then + echo "" + echo "Error: Abandoning installation due to errors in configuration file:" + cat $tmp/err + exit 1 + fi + + echo + echo "Enter the refresh interval (in seconds) [$refresh] \c" + read ans + if [ ! -z "$ans" ] + then + refresh=$ans + fi + + echo + echo "Do you want to modify the configuration predicate or refresh interval" + echo "at run time [$storable]? \c" + read ans + if [ ! -z "$ans" ] + then + case $ans in + N|n|NO|No|no) storable=n;; + Y|y|YES|Yes|yes) storable=y;; + esac + fi + if [ $storable = y ] + then + store_opt="" + else + store_opt="-s" + fi + + if [ "$do_debug" = true ] + then + echo + echo "Enter the debugging flag (see pmdbg(1)) [$debug] \c" + read ans + if [ ! -z "$ans" ] + then + debug=$ans + fi + fi + + args="$store_opt -t $refresh -D $debug $configfile" +fi + +pmdaInstall +echo "Please note that instance related metrics will not be available" +echo "until after the initial refresh of $refresh seconds." + +status=0 +exit 0 diff --git a/src/pmdas/hotproc/README b/src/pmdas/hotproc/README new file mode 100644 index 0000000..be8c5a4 --- /dev/null +++ b/src/pmdas/hotproc/README @@ -0,0 +1,141 @@ +Performance Co-Pilot hotproc PMDA for Active Process Monitoring +=============================================================== + +This PMDA is designed to be configurable to monitor processes which +the administrator deems "hot" or "interesting." The PMDA is similar +to the proc PMDA except in two main aspects: + +(i) it extends the proc metric set by: + hotproc.cpuburn, + hotproc.control.*, + hotproc.predicate.*, + hotproc.total.* . + +(ii) it allows one to retrieve all the instances. + +It is allowed to retrieve all the instances because the set of +instances is restricted by a predicate specified in a configuration +file. The predicate specifies what processes are "interesting", for +example, + + (cpuburn > 0.1 && uname == "root") + +and it applies this predicate every seconds. + +Therefore, hotproc.nprocs now refers to the number of "interesting" +processes instead of the list of all the processes. + +To monitor how successful (according to activity) that the +configuration predicate and refresh interval are, the hotproc.total.* +metrics can be used. For example, hotproc.total.cpuother.transient +shows how much of the cpu that transient processes (ones which do not +live for the refresh interval) get. If one is interested in some of +these processes then reducing the refresh interval may catch them. +Hotproc.total.cpuother.not_cpuburn indicates how much of the cpu that +the "uninteresting" processes are getting. On the basis of this value, +one may decide to change what is "interesting" by modifying the +configuration predicate. If one wants to get a simple indication of how +much of the cpu that all of the transient and "uninteresting" processes +are getting, then hotproc.total.cpuother.percent is the answer. + +In order to see why the instances (processes) of the hotproc agent +were chosen, one can check the hotproc.predicate.* metrics. These +metrics show the values used by the predicate evaluation at the +last refresh of the instance domain. For example, if one used a +predicate of (syscalls > 100), then doing: + $ pminfo -f hotproc.predicate.syscalls +will show the values of the system call rates of the processes +which satisfy the predicate (i.e. are greater than 100 per second +over the last refresh interval). + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT hotproc + +Installation of the hotproc PMDA +================================ + + + # cd $PCP_PMDAS_DIR/hotproc + + + Check that there is no clash with the Performance Metrics Domain + number defined in domain.h and the other PMDAs currently in use + (see $PCP_PMCDCONF_PATH). If there is, edit domain.h and choose + another domain number. + + + Inspect the ./sample.conf file and either modify it or create a new + configuration file that suits your need for "interesting". See + pmdahotproc(1) for configuration specification. + + + Then run the Install script (as root) + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + Answer the questions, which include the option to specify the + configuration file that you created. You will also need to specify + a refresh interval which determines how often the "hot" predicate + is used over the current set of processes. A smaller number will + mean that the predicate will be able to choose processes which have + short lives or sporadic activity but will consume more CPU because + it is run more often. + + + At the end of installation a check is made to verify that the + metrics of the agent can be retrieved. The reported number from this + check will be low because most of the hotproc metrics will not be + available until after the first refresh interval. + +Special TRIX Installation Considerations +======================================== + + For SGI Trix systems, the hotproc PMDA needs the CAP_MAC_READ + capability in addition to the default capability (CAP_SCHED_MGT), + before it can interrogate the resource utilization of all processes. + + To achieve this, run the ./Install script as described above, then + + 1. edit /etc/pmcd.conf and for the pmdahotproc line, replace the + pmda invocation arguments + $PCP_PMDAS_DIR/hotproc/pmdahotproc ... + by + /sbin/suattr -C CAP_SCHED_MGT,CAP_MAC_READ+ipe -c "$PCP_PMDAS_DIR/hotproc/pmdahotproc ..." + + 2. restart pmcd + # /etc/init.d/pcp start + + Thanks to Roald Lygre for this recipe. + +De-installation +=============== + +Simply use + + # cd $PCP_PMDAS_DIR/hotproc + # ./Remove + +Changing the settings +===================== + +The refresh period can be dynamically modified using +pmstore(1) for the metric hotproc.control.refresh. + +To make permanent changes, re-run the Install script. + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/hotproc.log) should be checked for any warnings + or errors. + + + If the Install script reports some warnings when checking the + metrics, the problem should be listed in one of the log files. diff --git a/src/pmdas/hotproc/Remove b/src/pmdas/hotproc/Remove new file mode 100644 index 0000000..37b3a89 --- /dev/null +++ b/src/pmdas/hotproc/Remove @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the sample PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=hotproc + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/hotproc/fixpmns.awk b/src/pmdas/hotproc/fixpmns.awk new file mode 100644 index 0000000..bf2d568 --- /dev/null +++ b/src/pmdas/hotproc/fixpmns.awk @@ -0,0 +1,35 @@ +# states +# 0 nothing interesting is happening, looking for proc { ... } +# 1 inside proc { ... }; want to copy all lines except +# "nprocs" metric that does not come from here for the +# hotproc PMDA +# 2 looking for proc.foo { ... } +# 3 inside proc.foo { ... }; want to copy all lines +# +BEGIN { print "/*" + print " * Hotproc Performance Metric Domain (PMD) Identifiers" + print " * (generated from pmns for hotproc and proc metrics)" + print " */" + print "" + print "hotproc {" + print " nprocs HOTPROC:100:0" + print " cpuburn HOTPROC:102:0" + print " total" + print " predicate" + print " control" + state = 0 + } + +state == 0 && /^proc / { state = 1; next } + +state == 1 && /nprocs/ { next } + +state == 1 && /^}/ { print; print ""; state = 2; next } + +state == 1 { print } + +state == 2 && /^proc\./ { state = 3 } + +state == 3 && /^}/ { print; print ""; state = 2; next } + +state == 3 { print } diff --git a/src/pmdas/hotproc/general.conf b/src/pmdas/hotproc/general.conf new file mode 100644 index 0000000..b51ca11 --- /dev/null +++ b/src/pmdas/hotproc/general.conf @@ -0,0 +1,27 @@ +#pmdahotproc +Version 1.0 + +# this configuration is of a more general nature, and identifies +# a number of "interesting" classes of processes ... it is assumed +# that some other monitoring tool, like pmie, would be able to +# decide which processes are members of each "interesting" class +# +# a sample pmie ruleset matching this hotproc configuration may +# be found in $PCP_PMDAS_DIR/hotproc/general.pmie +# + + cpuburn > 0.95 # one process using more than the equivalent of + # 95% of one processor + +|| iodemand > 500 # or more than 500 Kbytes/sec passing across the + # read() and write() system call interfaces + +|| syscalls > 100 # or more than 100 system calls/sec + +|| iowait > 0.33 # or more than 33% of the time is waiting for + # some sort of I/O + +|| schedwait > 0.25 # or more than 25% of the time is on the run + # queue waiting for the scheduler to assign + # a processor + diff --git a/src/pmdas/hotproc/general.pmie b/src/pmdas/hotproc/general.pmie new file mode 100755 index 0000000..6f4929c --- /dev/null +++ b/src/pmdas/hotproc/general.pmie @@ -0,0 +1,29 @@ +// this pmie configuration files assumes the hotproc PMDA has been +// configured with the "general.conf" configuration file, and thus +// is using as predicate something like: +// +// cpuburn > 0.95 || iodemand > 500 || syscalls > 100 +// || iowait > 0.33 || schedwait > 0.25 +// + +delta = 60sec; // change this if the hotproc PMDA refresh interval + // is different to the default of 60 seconds + +some_inst 100 * hotproc.psinfo.time > 95 + -> print "\nCPU: %v% busy %i"; + +io_bytes = "hotproc.accounting.counts"; +some_inst + (($io_bytes.chr + $io_bytes.chw) / 1024) > 500 + -> print "\nI/O: %v Kbyte/sec %i"; + +some_inst hotproc.psusage.sysc > 100 + -> print "\nsyscall: %v call/sec %i"; + +io_timers = "hotproc.accounting.timers"; +some_inst + 100 * ( $io_timers.bwtime + $io_timers.rwtime ) > 33 + -> print "\nI/O: %v% waiting %i"; + +some_inst 100 * $io_timers.qwtime > 25 + -> print "\nCPU: %v% runq waiting %i"; diff --git a/src/pmdas/hotproc/help.fmt b/src/pmdas/hotproc/help.fmt new file mode 100755 index 0000000..1e44ccd --- /dev/null +++ b/src/pmdas/hotproc/help.fmt @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Run paragraphs of help text thru fmt(1) +# Lines with tabs or double spaces are run thru fmt(1) only for that line + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +$PCP_AWK_PROG -v tmpfile=$tmp/fmt ' + function fmt_line(l) { + print l > tmpfile + system("fmt -78 < " tmpfile) # format line(s) + close(tmpfile) + system(">" tmpfile) # truncate file + } + function format_line() { + if (line != "") { + fmt_line(line) + line = "" + } + } + BEGIN { line = "" } + /^#/ || /^@/ { format_line(); print; next } + / / || / / { format_line(); fmt_line($0); next } + /^$/ { format_line(); print; next } + { + if (line == "") + line = $0 + else + line = sprintf("%s\n%s",line,$0) + } + END { format_line() } +' diff --git a/src/pmdas/hotproc/help.hotproc b/src/pmdas/hotproc/help.hotproc new file mode 100644 index 0000000..30a74da --- /dev/null +++ b/src/pmdas/hotproc/help.hotproc @@ -0,0 +1,141 @@ +@ hotproc.cpuburn CPU utilization per "interesting" process +CPU utilization, or the fraction of time that each "interesting" process +was executing (user and system time) over the last refresh interval. +Also known as the "cpuburn" time. + +@ hotproc.control.refresh time in secs between refreshes +Controls how long it takes before the "interesting" process list is refreshed +and new cpuburn times (see hotproc.cpuburn) calculated. This value can be +changed at any time by using pmstore(1) if the permission is given during +installation of the hotproc PMDA. Once the value is changed, the instances +will not be available until after the new refresh period has elapsed. + +@ hotproc.control.config configuration predicate +The configuration predicate that is used to characterize "interesting" +processes. This will initially be the predicate as specified in the +configuration file. This value can be changed at any time by using +pmstore(1) if the permission is given during installation of the hotproc +PMDA. Once the value is changed, the instances will not be available +until after the refresh period has elapsed. + +@ hotproc.control.config_gen configuration generation number +Each time the configuration predicate is updated (see hotproc.control.config) +the configuration generation number is incremented. + +@ hotproc.total.cpuburn total amount of cpuburn over all "interesting" processes +The sum of the CPU utilization ("cpuburn" or the fraction of time that each +process was executing in user or system mode over the last refresh interval) +for all the "interesting" processes. + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuidle fraction of CPU idle time +The fraction of all CPU time classified as idle over the last refresh +interval. + +@ hotproc.total.cpuother.not_cpuburn total amount of cpuburn over all uninteresting processes +The sum of the CPU utilization ("cpuburn" or the fraction of time that +each process was executing in user or system mode over the last refresh +interval) for all the "uninteresting" processes. If this value is high in +comparison to hotproc.total.cpuburn, then configuration predicate of the +hotproc PMDA is classifying a significant fraction of the CPU utilization +to processes that are not "interesting". + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuother.transient fraction of time utilized by "transient" processes +The total CPU utilization (fraction of time that each process was executing +in user or system mode) for processes which are not present throughout +the most recent refreshes interval. The hotproc PMDA is limited to +selecting processes which are present throughout each refresh intervals. +If processes come and/or go during a refresh interval then they will never +be considered. This metric gives an indication of the level of activity of +these "transient" processes. If the value is large in comparison to the +sum of hotproc.total.cpuburn and hotproc.total.cpuother.not_cpuburn then +the "transient" processes are consuming lots of CPU time. Under these +circumstances, the hotproc PMDA may be less useful, or consideration +should be given to decreasing the value of the refresh interval +(hotproc.control.refresh) so fewer "transient" processes escape +consideration. + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuother.total total amount of cpuburn other than the "interesting" processes +Non-idle CPU utilization not accounted for by processes other than those +deemed "interesting". It is equivalent to hotproc.total.cpuother.not_cpuburn ++ hotproc.total.cpuother.transient. + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuother.percent how much of the cpu for "transients" and uninterestings +Gives an indication of how much of the CPU time the "transient" processes +and the "uninteresting" processes are accounting for. Computed as: + 100 * hotproc.total.cpuother.total / number of CPUs + +@ hotproc.predicate.syscalls number of system calls per second over refresh interval +The number of system calls per second over the last refresh interval for +each "interesting" process. If the refresh interval spans times from t1 +to t2, then this is calculated by: + + m = hotproc.psusage.pu_sysc + + (m@t2 - m@t1) / (t2 - t1) + +@ hotproc.predicate.ctxswitch number of context switches per second over refresh interval +The number of context switches per second over the last refresh interval +for each "interesting" process. If the refresh interval spans times from +t1 to t2, then this is calculated by: + + m = hotproc.psusage.pu_vctx + hotproc.psusage.pu_ictx + + (m@t2 - m@t1) / (t2 - t1) + +@ hotproc.predicate.virtualsize virtual size of process in kilobytes at last refresh +The virtual size of each "interesting" process in kilobytes at the last +refresh time, calculated by: + + hotproc.psinfo.size + +@ hotproc.predicate.residentsize resident size of process in kilobytes at last refresh +The resident size of each "interesting" process in kilobytes at the last +refresh, calculated by: + + hotproc.psinfo.rssize + +@ hotproc.predicate.iodemand total kilobytes read and written per second over refresh interval +The total kilobytes read and written per second over the last refresh +interval for each "interesting" process. If the refresh interval spans +times from t1 to t2, then this is calculated by: + + // bytes read() + br = hotproc.psusage.bread@t2 - hotproc.psusage.bread@t1 + // Gigabytes read() + gbr = hotproc.psusage.gbread@t2 - hotproc.psusage.gbread@t1 + // bytes write() + bw = hotproc.psusage.bwrit@t2 - hotproc.psusage.bwrit@t1 + // Gigabytes write() + gbw = hotproc.psusage.gbwrit@t2 - hotproc.psusage.gbwrit@t1 + + ((gbr + gbw) * 1024 * 1024 + (br + bw) / 1024 ) / (t2 - t1) + +@ hotproc.predicate.iowait time in secs waiting for I/O per second over refresh interval +The fraction of time waiting for I/O for each "interesting" process over +refresh interval. If the refresh interval spans times from t1 to t2, +then this is calculated by: + + bw = (hotproc.accounting.timers.bwtime@t2 - + hotproc.accounting.timers.bwtime@t1) / 1000000000 + rw = (hotproc.accounting.timers.rwtime@t2 - + hotproc.accounting.timers.rwtime@t1) / 1000000000 + + (bw + rw) / (t2 - t1) + +@ hotproc.predicate.schedwait time in secs waiting on run queue per second over refresh interval +The fraction of time waiting on the run queue for each "interesting" +process over the last refresh interval. If the refresh interval spans +times from t1 to t2, then this is calculated by: + + qw = (hotproc.accounting.timers.qwtime@t2 - + hotproc.accounting.timers.qwtime@t1) / 1000000000 + + qw / (t2 - t1) diff --git a/src/pmdas/hotproc/pmns.hotproc b/src/pmdas/hotproc/pmns.hotproc new file mode 100644 index 0000000..0ea5a0e --- /dev/null +++ b/src/pmdas/hotproc/pmns.hotproc @@ -0,0 +1,34 @@ + +/* + * Metrics borrowed from "proc" pmda above ... these ones are specific + * to the hotproc PMDA. + */ + +hotproc.control { + refresh HOTPROC:100:1 + config HOTPROC:100:8 + config_gen HOTPROC:100:9 +} + +hotproc.total { + cpuidle HOTPROC:100:2 + cpuburn HOTPROC:100:3 + cpuother +} + +hotproc.total.cpuother { + transient HOTPROC:100:4 + not_cpuburn HOTPROC:100:5 + total HOTPROC:100:6 + percent HOTPROC:100:7 +} + +hotproc.predicate { + syscalls HOTPROC:101:0 + ctxswitch HOTPROC:101:1 + virtualsize HOTPROC:101:2 + residentsize HOTPROC:101:3 + iodemand HOTPROC:101:4 + iowait HOTPROC:101:5 + schedwait HOTPROC:101:6 +} diff --git a/src/pmdas/hotproc/root b/src/pmdas/hotproc/root new file mode 100644 index 0000000..0e14a7d --- /dev/null +++ b/src/pmdas/hotproc/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { hotproc } + +#include "pmns" + diff --git a/src/pmdas/hotproc/sample.conf b/src/pmdas/hotproc/sample.conf new file mode 100644 index 0000000..6421e51 --- /dev/null +++ b/src/pmdas/hotproc/sample.conf @@ -0,0 +1,15 @@ +#pmdahotproc +Version 1.0 + +# Sample configuration file for hotproc PMDA +# +# It selects processes who are using more than 85% of the cpu +# over the refresh interval. + +cpuburn > 0.85 + +# Another example: +# Reporting processes which are either +# using greater than 85% cpu or +# are root processes using greater than 1% cpu +# ((cpuburn > 0.01) && (uname == "root")) || (cpuburn > 0.85) diff --git a/src/pmdas/hotproc/src/GNUmakefile b/src/pmdas/hotproc/src/GNUmakefile new file mode 100644 index 0000000..6000032 --- /dev/null +++ b/src/pmdas/hotproc/src/GNUmakefile @@ -0,0 +1,34 @@ +#!gmake +# +# Copyright (c) 2007 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +# This is just to get the source exported to the community. +# 'Makefile' in particular is included only for clues it might offer, +# and should almost certainly be removed from SRCFILES once +# the pmda is working. +LSRCFILES = Makefile config.c config.h ctltab.c error.c gram.y \ +gram_node.c gram_node.h hotproc.c hotproc.h lex.l pcpu.c pcpu.h \ +pglobal.c pglobal.h ppred_values.c ppred_values.h + +default default_pcp: +install install_pcp: +include $(BUILDRULES) + diff --git a/src/pmdas/hotproc/src/config.c b/src/pmdas/hotproc/src/config.c new file mode 100644 index 0000000..8710810 --- /dev/null +++ b/src/pmdas/hotproc/src/config.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gram_node.h" +#include "gram.h" +#include "nameinfo.h" +#include "config.h" + +char *conf_buffer = NULL; /* contains config text */ +char *conf_buffer_ptr = NULL; +char *pred_buffer = NULL; /* contains parsed predicate */ + +static bool_node *the_tree = NULL; +static config_vars *the_vars = NULL; + +/* internal functions */ +static int eval_predicate(bool_node *); +static int eval_comparison(bool_node *); +static int eval_num_comp(N_tag, bool_node *, bool_node *); +static int eval_str_comp(N_tag, bool_node *, bool_node *); +static int eval_match_comp(N_tag, bool_node *, bool_node *); +static char* get_strvalue(bool_node *); +static double get_numvalue(bool_node *); +static void eval_error(char *); + +extern int parse_predicate(bool_node **); +extern char *pmProgname; +extern char *configfile; +extern FILE *yyin; + + + +FILE * +open_config(void) +{ + FILE *conf; + if ((conf = fopen(configfile, "r")) == NULL) { + (void)fprintf(stderr, "%s: Unable to open configuration file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + return conf; +} + +int +parse_config(bool_node **tree) +{ + int sts; + FILE *file = NULL; + char *fname; + struct stat stat_buf; + long size; + char *ptr; + + if ((sts = parse_predicate(tree)) != 0) { + (void)fprintf(stderr, "%s: Failed to parse configuration file\n", pmProgname); + return sts; + } + + /* --- dump to tmp file & read to buffer --- */ + if ((fname = tmpnam(NULL)) == NULL || + (file = fopen(fname, "w+")) == NULL) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to create \"%s\": %s\n", + pmProgname, fname, strerror(-sts)); + goto error; + } + if (unlink(fname) == -1) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to unlink \"%s\": %s\n", + pmProgname, fname, strerror(-sts)); + goto error; + } + dump_predicate(file, *tree); + fflush(file); + if (fstat(fileno(file), &stat_buf) < 0) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to stat \"%s\": %s\n", + pmProgname, fname, strerror(-sts)); + goto error; + } + size = (long)stat_buf.st_size; + ptr = malloc(size+1); + if (ptr == NULL) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to malloc: %s\n", + pmProgname, strerror(-sts)); + goto error; + } + rewind(file); + if (fread(ptr, size, 1, file) != 1) { + clearerr(file); + fprintf(stderr, "%s: parse_config: failed to fread \"%s\"\n", + pmProgname, fname); + sts = -1; + goto error; + } + (void)fclose(file); + + if (pred_buffer != NULL) + free(pred_buffer); + pred_buffer = ptr; + pred_buffer[size] = '\0'; + return 0; + +error: + if (file != NULL) + (void)fclose(file); + return sts; +} + +void +new_tree(bool_node *tree) +{ + free_tree(the_tree); + the_tree = tree; +} + +void +read_config(FILE *conf) +{ + struct stat stat_buf; + long size; + int sts; + size_t nread; + + /* get length of file */ + sts = fstat(fileno(conf), &stat_buf); + if (sts < 0) { + (void)fprintf(stderr, "%s: Failure to stat configuration file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + size = (long)stat_buf.st_size; + + /* create buffer */ + conf_buffer = (char*)malloc(size+1*sizeof(char)); + if (conf_buffer == NULL) { + (void)fprintf(stderr, "%s: Failure to create buffer for configuration file \"%s\"\n", + pmProgname, configfile); + exit(1); + } + + conf_buffer_ptr = conf_buffer; + + /* read whole file into buffer */ + nread = fread(conf_buffer, sizeof(char), size, conf); + if (nread != size) { + (void)fprintf(stderr, "%s: Failure to read configuration file \"%s\" into buffer\n", + pmProgname, configfile); + exit(1); + } + conf_buffer[size] = '\0'; /* terminate the buffer */ + + if (parse_config(&the_tree) != 0) + exit(1); +} + +void +dump_tree(FILE *f) +{ + dump_bool_tree(f, the_tree); +} + + +int +eval_tree(config_vars *vars) +{ + the_vars = vars; + return eval_predicate(the_tree); +} + +/* + * do predicate testing for qa + */ + +#define QA_LINE 512 + +/* + * Return convention + * EOF = finished line or file + * 0 = error in input + * 1 = successful and continue + */ + +/* + * Read test vars of form: "var=value|var=value|var=value" + */ + +static int +read_test_var(char *line, config_vars *vars) +{ + const char EQUALS = '='; + const char DIVIDER = '|'; + char var[QA_LINE]; + char value[QA_LINE]; + static char *c; + int i = 0; + + /* if line is NULL then continue where left off */ + if (line != NULL) + c = line; + + if (*c == '\n') + return EOF; + + /* --- get variable name --- */ + i = 0; + while(*c != EQUALS && *c != '\n') { + var[i++] = *c++; + } + var[i] = '\0'; + + if (*c == '\n') { + fprintf(stderr, "%s: Error reading test variable, " + "looking for \"%c\"\n", pmProgname, EQUALS); + return 0; + } + + c++; /* skip over EQUALS */ + + /* --- get value --- */ + i = 0; + while(*c != DIVIDER && *c != '\n') { + value[i++] = *c++; + } + value[i] = '\0'; + + if (*c == DIVIDER) /* skip over DIVIDER */ + c++; + + /* --- var = value --- */ + if (strcmp(var, "uid") == 0) { + vars->uid = atoi(value); + } + else if (strcmp(var, "uname") == 0) { + if ((vars->uname = strdup(value)) == NULL) + goto failure; + } + else if (strcmp(var, "gid") == 0) { + vars->gid = atoi(value); + } + else if (strcmp(var, "gname") == 0) { + if ((vars->gname = strdup(value)) == NULL) + goto failure; + } + else if (strcmp(var, "fname") == 0) { + (void)strcpy(vars->fname, value); + } + else if (strcmp(var, "psargs") == 0) { + (void)strcpy(vars->psargs, value); + } + else if (strcmp(var, "cpuburn") == 0) { + vars->cpuburn = atof(value); + } + else if (strcmp(var, "syscalls") == 0) { + vars->preds.syscalls = atof(value); + } + else if (strcmp(var, "pu_sysc") == 0) { + vars->pu_sysc = atol(value); + } + else if (strcmp(var, "ctxswitch") == 0) { + vars->preds.ctxswitch = atof(value); + } + else if (strcmp(var, "pu_vctx") == 0) { + vars->pu_vctx = atol(value); + } + else if (strcmp(var, "pu_ictx") == 0) { + vars->pu_ictx = atol(value); + } + else if (strcmp(var, "virtualsize") == 0) { + vars->preds.virtualsize = atof(value); + } + else if (strcmp(var, "pr_size") == 0) { + vars->pr_size = atol(value); + } + else if (strcmp(var, "residentsize") == 0) { + vars->preds.residentsize = atof(value); + } + else if (strcmp(var, "pr_rssize") == 0) { + vars->pr_rssize = atol(value); + } + else if (strcmp(var, "iodemand") == 0) { + vars->preds.iodemand = atof(value); + } + else if (strcmp(var, "pu_gbread") == 0) { + vars->pu_gbread = atol(value); + } + else if (strcmp(var, "pu_bread") == 0) { + vars->pu_bread = atol(value); + } + else if (strcmp(var, "pu_gbwrit") == 0) { + vars->pu_gbwrit = atol(value); + } + else if (strcmp(var, "pu_bwrit") == 0) { + vars->pu_bwrit = atol(value); + } + else if (strcmp(var, "iowait") == 0) { + vars->preds.iowait = atof(value); + } + else if (strcmp(var, "ac_bwtime") == 0) { + vars->ac_bwtime = atoll(value); + } + else if (strcmp(var, "ac_rwtime") == 0) { + vars->ac_rwtime = atoll(value); + } + else if (strcmp(var, "schedwait") == 0) { + vars->preds.schedwait = atof(value); + } + else if (strcmp(var, "ac_qwtime") == 0) { + vars->ac_qwtime = atoll(value); + } + else { + fprintf(stderr, "%s: Error unrecognised test variable: \"%s\"\n", + pmProgname, var); + return 0; + } + + return 1; + +failure: + (void)fprintf(stderr, "%s: malloc failed for read_test_var()\n", pmProgname); + exit(1); +} + + +int +read_test_values(FILE *file, config_vars *vars) +{ + static char line[QA_LINE]; + int sts; + int i; + + if (fgets(line, QA_LINE-1, file) == NULL) + return EOF; + + if (strlen(line) == QA_LINE-1) { + fprintf(stderr, "%s: line limit exceeded\n", pmProgname); + return 0; + } + + /* note that line must end in '\n' */ + + /* reset all values */ + (void)memset(vars, 0, sizeof(*vars)); + + /* read each var=value pair for a line */ + for(i=0;/*forever*/;i++) { + sts = read_test_var((i==0?line:NULL), vars); + if (sts == EOF) + return 1; + if (sts == 0) + return 0; + } +} + + + +void +do_pred_testing(void) +{ + int sts; + config_vars vars; + FILE *conf = NULL; + + conf = open_config(); + read_config(conf); + (void)fclose(conf); + + dump_tree(stdout); + + for (;;) { + sts = read_test_values(stdin, &vars); + if (sts == EOF) + break; + if (sts == 0) { + (void)fprintf(stderr, "Bad input line\n"); + continue; + } + + if (eval_tree(&vars)) { + (void)fprintf(stdout, "true\n"); + } + else { + (void)fprintf(stdout, "false\n"); + } + } +} + + + +static void +eval_error(char *msg) +{ + (void)fprintf(stderr, "%s: Internal error : %s\n", pmProgname, msg?msg:""); + exit(1); +} + +static int +eval_predicate(bool_node *pred) +{ + bool_node *lhs, *rhs; + + switch(pred->tag) { + case N_and: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + return eval_predicate(lhs) && eval_predicate(rhs); + case N_or: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + return eval_predicate(lhs) || eval_predicate(rhs); + case N_not: + lhs = pred->data.children.left; + return !eval_predicate(lhs); + case N_true: + return 1; + case N_false: + return 0; + default: + return eval_comparison(pred); + }/*switch*/ +} + +static int +eval_comparison(bool_node *comp) +{ + bool_node *lhs = comp->data.children.left; + bool_node *rhs = comp->data.children.right; + + switch(comp->tag) { + case N_lt: case N_gt: case N_ge: case N_le: + case N_eq: case N_neq: + return eval_num_comp(comp->tag, lhs, rhs); + case N_seq: case N_sneq: + return eval_str_comp(comp->tag, lhs, rhs); + case N_match: case N_nmatch: + return eval_match_comp(comp->tag, lhs, rhs); + default: + eval_error("comparison"); + }/*switch*/ +} + +static int +eval_num_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + double x = get_numvalue(lhs); + double y = get_numvalue(rhs); + + switch(tag) { + case N_lt: return (x < y); + case N_gt: return (x > y); + case N_le: return (x <= y); + case N_ge: return (x >= y); + case N_eq: return (x == y); + case N_neq: return (x != y); + default: + eval_error("number comparison"); + }/*switch*/ +} + +static double +get_numvalue(bool_node *n) +{ + switch(n->tag) { + case N_number: return n->data.num_val; + case N_cpuburn: return the_vars->cpuburn; + case N_syscalls: return the_vars->preds.syscalls; + case N_ctxswitch: return the_vars->preds.ctxswitch; + case N_virtualsize: return the_vars->preds.virtualsize; + case N_residentsize: return the_vars->preds.residentsize; + case N_iodemand: return the_vars->preds.iodemand; + case N_iowait: return the_vars->preds.iowait; + case N_schedwait: return the_vars->preds.schedwait; + case N_gid: return the_vars->gid; + case N_uid: return the_vars->uid; + default: + eval_error("number value"); + } +} + +static int +eval_str_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + char *x = get_strvalue(lhs); + char *y = get_strvalue(rhs); + + switch(tag) { + case N_seq: return (strcmp(x,y)==0?1:0); + case N_sneq: return (strcmp(x,y)==0?0:1); + default: + eval_error("string comparison"); + }/*switch*/ +} + +static int +eval_match_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + int sts; + char *res; + char *str= get_strvalue(lhs); + char *pat = get_strvalue(rhs); + + if (rhs->tag != N_pat) { + eval_error("match"); + } + + res = re_comp(pat); + if (res != NULL) { + /* should have been checked at lex stage */ + /* => internal error */ + eval_error(res); + } + sts = re_exec(str); + if (sts < 0) { + eval_error("re_exec"); + } + + switch(tag) { + case N_match: return sts; + case N_nmatch: return !sts; + default: + eval_error("match comparison"); + }/*switch*/ +} + +static char * +get_strvalue(bool_node *n) +{ + + switch(n->tag) { + case N_str: + case N_pat: + return n->data.str_val; + case N_gname: + if (the_vars->gname != NULL) + return the_vars->gname; + else + return get_gname_info(the_vars->gid); + case N_uname: + if (the_vars->uname != NULL) + return the_vars->uname; + else + return get_uname_info(the_vars->uid); + case N_fname: return the_vars->fname; + case N_psargs: return the_vars->psargs; + default: + eval_error("string value"); + }/*switch*/ +} diff --git a/src/pmdas/hotproc/src/config.h b/src/pmdas/hotproc/src/config.h new file mode 100644 index 0000000..7de4186 --- /dev/null +++ b/src/pmdas/hotproc/src/config.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include + +typedef struct { + double syscalls; + double ctxswitch; + double virtualsize; + double residentsize; + double iodemand; + double iowait; + double schedwait; +} derived_pred_t; + + +typedef struct { + uid_t uid; /* real user id */ + gid_t gid; /* real group id */ + char *uname; + char *gname; + char fname[PRCOMSIZ]; /* basename of exec()'d pathname */ + char psargs[PRARGSZ]; /* initial chars of arg list */ + double cpuburn; + + derived_pred_t preds; + + /* --- ioctl buffer fields for testing purposes only --- */ + + /* prpsinfo_t fields */ + ulong_t pr_size; + ulong_t pr_rssize; + + /* prusage_t fields */ + ulong_t pu_sysc; + ulong_t pu_ictx; + ulong_t pu_vctx; + ulong_t pu_gbread; + ulong_t pu_bread; + ulong_t pu_gbwrit; + ulong_t pu_bwrit; + + /* accounting fields */ + accum_t ac_bwtime; + accum_t ac_rwtime; + accum_t ac_qwtime; + +} config_vars; + +#include "gram_node.h" + +FILE *open_config(void); +void read_config(FILE *); +int parse_config(bool_node **tree); +void new_tree(bool_node *tree); +int eval_tree(config_vars *); +void dump_tree(FILE *); +void do_pred_testing(void); +int read_test_values(FILE *, config_vars *); + +#endif diff --git a/src/pmdas/hotproc/src/ctltab.c b/src/pmdas/hotproc/src/ctltab.c new file mode 100644 index 0000000..534cbe0 --- /dev/null +++ b/src/pmdas/hotproc/src/ctltab.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "pmapi.h" +#include "ctltab.h" +#include "pglobal.h" +#include "pmemory.h" +#include "pracinfo.h" +#include "pscred.h" +#include "psinfo.h" +#include "pstatus.h" +#include "psusage.h" +#include "pcpu.h" +#include "ppred_values.h" + +ctltab_entry ctltab[] = { + { CLUSTER_GLOBAL, 1, + pglobal_init, pglobal_getdesc, pglobal_setatom, pglobal_getinfo, pglobal_allocbuf }, + { 1, 1, + psinfo_init, psinfo_getdesc, psinfo_setatom, psinfo_getinfo, psinfo_allocbuf }, + { 2, 1, + pstatus_init, pstatus_getdesc, pstatus_setatom, pstatus_getinfo, pstatus_allocbuf }, + { 3, 1, + pscred_init, pscred_getdesc, pscred_setatom, pscred_getinfo, pscred_allocbuf }, + { 4, 1, + psusage_init, psusage_getdesc, psusage_setatom, psusage_getinfo, psusage_allocbuf }, + { 5, 1, + pmem_init, pmem_getdesc, pmem_setatom, pmem_getinfo, pmem_allocbuf }, + { 6, 1, + pracinfo_init, pracinfo_getdesc, pracinfo_setatom, pracinfo_getinfo, pracinfo_allocbuf }, + { CLUSTER_CPU, 1, + pcpu_init, pcpu_getdesc, pcpu_setatom, pcpu_getinfo, pcpu_allocbuf }, + { CLUSTER_PRED, 1, + ppred_init, ppred_getdesc, ppred_setatom, ppred_getinfo, ppred_allocbuf }, +}; + + +int nctltab = sizeof(ctltab) / sizeof (ctltab[0]); + +int +lookup_ctltab(int cluster) +{ + int i; + + for (i = 0; i < nctltab; i++) { + if (ctltab[i].cluster == cluster) + return i; + } + return PM_ERR_PMID; +} diff --git a/src/pmdas/hotproc/src/error.c b/src/pmdas/hotproc/src/error.c new file mode 100644 index 0000000..f71c5db --- /dev/null +++ b/src/pmdas/hotproc/src/error.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +void +yywarn(s) +char *s; +{ + extern int yylineno; + + (void)fprintf(stderr, "Warning [line %d]\n%s\n", yylineno, s); +} + +void +yyerror(s) +char *s; +{ + extern int yylineno; + extern char yytext[]; + + (void)fprintf(stderr, "Specification error in configuration\n"); + (void)fprintf(stderr, "[line %d] %s: %s\n", yylineno, s, yytext); +} diff --git a/src/pmdas/hotproc/src/gram.y b/src/pmdas/hotproc/src/gram.y new file mode 100644 index 0000000..b98564f --- /dev/null +++ b/src/pmdas/hotproc/src/gram.y @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%{ +#include +#include "./gram_node.h" + +void yyerror(char *s); +int yylex(void); +int yyparse(void); + +int need_psusage = 0; +int need_accounting = 0; +static bool_node *pred_tree = NULL; + +%} + +%union { + char *y_str; + double y_number; + bool_node *y_node; + } + +%token + NUMBER + +%token + STRING + PATTERN + +%term AND OR NOT + LPAREN RPAREN TRUE FALSE + EQUAL NEQUAL + LTHAN LEQUAL GTHAN GEQUAL + MATCH NMATCH + GID UID CPUBURN GNAME UNAME FNAME PSARGS + SYSCALLS CTXSWITCH VIRTUALSIZE RESIDENTSIZE + IODEMAND IOWAIT SCHEDWAIT + VERSION + +%type + predicate comparison + num_compar numvar + str_compar strvar + pattern_compar + +%type + version + +%left OR +%left AND +%left NOT + +%% + +pred_tree: predicate { pred_tree = $1;} + | version predicate { pred_tree = $2;} + ; + +version: VERSION NUMBER { + float version_num = $2; + + if (version_num != 1.0) { + (void)fprintf(stderr, "Wrong version number in configuration predicate\n"); + (void)fprintf(stderr, "Expected version %.2f, but was given version %.2f .\n", + 1.0, version_num); + YYABORT; + } + } + +predicate: + predicate AND predicate { $$ = create_tnode(N_and, $1, $3); } + | predicate OR predicate { $$ = create_tnode(N_or, $1, $3); } + | NOT predicate { $$ = create_tnode(N_not, $2, NULL); } + | LPAREN predicate RPAREN { $$ = $2; } + | comparison + | TRUE { $$ = create_tag_node(N_true); } + | FALSE { $$ = create_tag_node(N_false); } + ; + +comparison: + num_compar + | str_compar + | pattern_compar + ; + +num_compar: + numvar LTHAN numvar { $$ = create_tnode(N_lt, $1, $3); } + | numvar LEQUAL numvar { $$ = create_tnode(N_le, $1, $3); } + | numvar GTHAN numvar { $$ = create_tnode(N_gt, $1, $3); } + | numvar GEQUAL numvar { $$ = create_tnode(N_ge, $1, $3); } + | numvar EQUAL numvar { $$ = create_tnode(N_eq, $1, $3); } + | numvar NEQUAL numvar { $$ = create_tnode(N_neq, $1, $3); } + ; + +numvar: NUMBER { $$ = create_number_node($1); } + | GID { $$ = create_tag_node(N_gid); } + | UID { $$ = create_tag_node(N_uid); } + | CPUBURN { $$ = create_tag_node(N_cpuburn); } + | SYSCALLS { need_psusage = 1; $$ = create_tag_node(N_syscalls); } + | CTXSWITCH { need_psusage = 1; $$ = create_tag_node(N_ctxswitch); } + | VIRTUALSIZE { $$ = create_tag_node(N_virtualsize); } + | RESIDENTSIZE { $$ = create_tag_node(N_residentsize); } + | IODEMAND { need_psusage = 1; $$ = create_tag_node(N_iodemand); } + | IOWAIT { need_accounting = 1; $$ = create_tag_node(N_iowait); } + | SCHEDWAIT { need_accounting = 1; $$ = create_tag_node(N_schedwait); } + ; + +str_compar: + strvar EQUAL strvar { $$ = create_tnode(N_seq, $1, $3); } + | strvar NEQUAL strvar { $$ = create_tnode(N_sneq, $1, $3); } + ; + +strvar: STRING { $$ = create_str_node($1); } + | GNAME { $$ = create_tag_node(N_gname); } + | UNAME { $$ = create_tag_node(N_uname); } + | FNAME { $$ = create_tag_node(N_fname); } + | PSARGS { $$ = create_tag_node(N_psargs); } + ; + +pattern_compar: + strvar MATCH PATTERN { $$ = create_tnode(N_match, $1, create_pat_node($3)); } + | strvar NMATCH PATTERN { $$ = create_tnode(N_nmatch, $1, create_pat_node($3)); } + ; + + +%% + +int +parse_predicate(bool_node **tree) +{ + int sts; + extern int yylineno; /* defined by lex */ + + yylineno=1; + + start_tree(); + sts = yyparse(); + + /* free any partial trees */ + if (sts != 0) { + free_tree(NULL); + return sts; + } + + *tree = pred_tree; + return 0; +} diff --git a/src/pmdas/hotproc/src/gram_node.c b/src/pmdas/hotproc/src/gram_node.c new file mode 100644 index 0000000..d2c36b5 --- /dev/null +++ b/src/pmdas/hotproc/src/gram_node.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "./gram_node.h" + +/* functions */ +static void dump_comparison(FILE *, bool_node *); +static void dump_var(FILE *, bool_node *); + +static bool_node *node_list = NULL; + +void start_tree(void) +{ + node_list = NULL; +} + +void free_tree(bool_node *root) +{ + bool_node *n, *next; + + if (root == NULL) + root = node_list; /* use last tree */ + + /* free all nodes in list */ + for (n = root; n != NULL; ) { + next = n->next; + if (n->tag == N_pat || n->tag == N_str) + free(n->data.str_val); + free(n); + n = next; + } + + if (root == node_list) + node_list = NULL; +} + +bool_node * +create_tag_node(N_tag tag) +{ + bool_node *new_node; + + new_node = (bool_node*)malloc(sizeof(bool_node)); + if (new_node == NULL) { + fprintf(stderr, "hotproc: malloc failed in config: %s", osstrerror()); + exit(1); + } + new_node->tag = tag; + + /* add to front of node-list */ + new_node->next = node_list; + node_list = new_node; + + return new_node; +} + +bool_node * +create_tnode(N_tag tag, bool_node *lnode, bool_node *rnode) +{ + bool_node *n = create_tag_node(tag); + n->data.children.left = lnode; + n->data.children.right = rnode; + return n; +} + +bool_node * +create_number_node(double x) +{ + bool_node *n = create_tag_node(N_number); + n->data.num_val = x; + return n; +} + + +bool_node *create_str_node(char *str) +{ + bool_node *n = create_tag_node(N_str); + n->data.str_val = str; + return n; +} + +bool_node *create_pat_node(char *str) +{ + bool_node *n = create_tag_node(N_pat); + n->data.str_val = str; + return n; +} + +void +dump_bool_tree(FILE *f, bool_node *tree) +{ + (void)fprintf(f, "--- bool tree ---\n"); + dump_predicate(f, tree); + (void)fprintf(f, "\n--- end bool tree ---\n"); +} + +void +dump_predicate(FILE *f, bool_node *pred) +{ + bool_node *lhs, *rhs; + + switch(pred->tag) { + case N_and: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + (void)fprintf(f, "("); + dump_predicate(f, lhs); + (void)fprintf(f, " && "); + dump_predicate(f, rhs); + (void)fprintf(f, ")"); + break; + case N_or: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + (void)fprintf(f, "("); + dump_predicate(f, lhs); + (void)fprintf(f, " || "); + dump_predicate(f, rhs); + (void)fprintf(f, ")"); + break; + case N_not: + lhs = pred->data.children.left; + (void)fprintf(f, "(! "); + dump_predicate(f, lhs); + (void)fprintf(f, ")"); + break; + case N_true: + (void)fprintf(f, "(true)"); + break; + case N_false: + (void)fprintf(f, "(false)"); + break; + default: + dump_comparison(f, pred); + }/*switch*/ +} + +static void +dump_comparison(FILE *f, bool_node *comp) +{ + bool_node *lhs = comp->data.children.left; + bool_node *rhs = comp->data.children.right; + + (void)fprintf(f, "("); + dump_var(f, lhs); + switch(comp->tag) { + case N_lt: (void)fprintf(f, " < "); break; + case N_gt: (void)fprintf(f, " > "); break; + case N_le: (void)fprintf(f, " <= "); break; + case N_ge: (void)fprintf(f, " >= "); break; + case N_eq: (void)fprintf(f, " == "); break; + case N_seq: (void)fprintf(f, " == "); break; + case N_sneq: (void)fprintf(f, " != "); break; + case N_neq: (void)fprintf(f, " != "); break; + case N_match: (void)fprintf(f, " ~ "); break; + case N_nmatch: (void)fprintf(f, " !~ "); break; + default: (void)fprintf(f, ""); break; + }/*switch*/ + dump_var(f, rhs); + (void)fprintf(f, ")"); +} + +static void +dump_var(FILE *f, bool_node *var) +{ + switch(var->tag) { + case N_str: (void)fprintf(f, "\"%s\"", var->data.str_val); break; + case N_pat: (void)fprintf(f, "\"%s\"", var->data.str_val); break; + case N_number: (void)fprintf(f, "%f", var->data.num_val); break; + case N_uid: (void)fprintf(f, "uid"); break; + case N_gid: (void)fprintf(f, "gid"); break; + case N_uname: (void)fprintf(f, "uname"); break; + case N_gname: (void)fprintf(f, "gname"); break; + case N_fname: (void)fprintf(f, "fname"); break; + case N_psargs: (void)fprintf(f, "psargs"); break; + case N_cpuburn: (void)fprintf(f, "cpuburn"); break; + case N_syscalls: (void)fprintf(f, "syscalls"); break; + case N_ctxswitch: (void)fprintf(f, "ctxswitch"); break; + case N_virtualsize: (void)fprintf(f, "virtualsize"); break; + case N_residentsize: (void)fprintf(f, "residentsize"); break; + case N_iodemand: (void)fprintf(f, "iodemand"); break; + case N_iowait: (void)fprintf(f, "iowait"); break; + case N_schedwait: (void)fprintf(f, "schedwait"); break; + default: (void)fprintf(f, ""); break; + }/*switch*/ +} diff --git a/src/pmdas/hotproc/src/gram_node.h b/src/pmdas/hotproc/src/gram_node.h new file mode 100644 index 0000000..996208b --- /dev/null +++ b/src/pmdas/hotproc/src/gram_node.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GRAM_NODE_H +#define GRAM_NODE_H + +/* --- types --- */ +typedef enum +{ + N_and, N_or, N_not, + N_lt, N_le, N_gt, N_ge, + N_eq, N_neq, N_seq, N_sneq, + N_match, N_nmatch, + N_str, N_pat, N_number, + N_uid, N_gid, N_uname, N_gname, + N_fname, N_psargs, N_cpuburn, + N_true, N_false, + N_syscalls, N_ctxswitch, + N_virtualsize, N_residentsize, + N_iodemand, N_iowait, N_schedwait +} N_tag; + +typedef struct +{ + struct bool_node *left; + struct bool_node *right; +} bool_children; + +typedef struct bool_node +{ + N_tag tag; + struct bool_node *next; + union { + bool_children children; + char *str_val; + double num_val; + } + data; +} bool_node; + +/* --- functions --- */ + +void free_tree(bool_node *); +void start_tree(void); + +bool_node *create_tnode(N_tag, bool_node *, bool_node *); +bool_node *create_tag_node(N_tag); +bool_node *create_number_node(double); +bool_node *create_str_node(char *); +bool_node *create_pat_node(char *); +void dump_bool_tree(FILE *, bool_node *); +void dump_predicate(FILE *, bool_node *); + +#endif diff --git a/src/pmdas/hotproc/src/hotproc.c b/src/pmdas/hotproc/src/hotproc.c new file mode 100644 index 0000000..50abf0d --- /dev/null +++ b/src/pmdas/hotproc/src/hotproc.c @@ -0,0 +1,1555 @@ +/* + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +/* local stuff */ +#include "./cluster.h" +#include "../domain.h" +#include "./proc.h" +#include "./proc_aux.h" +#include "./psinfo.h" +#include "./psusage.h" +#include "./pglobal.h" +#include "./pcpu.h" +#include "./ctltab.h" +#include "./config.h" +#include "./hotproc.h" +#include "./pracinfo.h" +#include "./ppred_values.h" + +#define MIN_REFRESH 1 +#define INIT_PROC_MAX 200 + +#if (_MIPS_SZLONG == 32) +#define IO_PMTYPE PM_TYPE_U32 /* ulong_t from procfs.h/prusage */ +#define CTX_PMTYPE PM_TYPE_U32 /* ulong_t from procfs.h/prusage */ +#define SYSCALLS_PMTYPE PM_TYPE_U32 /* ulong_t from procfs.h/prusage */ +#define SYSIDLE_PMTYPE PM_TYPE_32 /* long=time_t from sysinfo.h/sysinfo */ +#define TIME_PMTYPE PM_TYPE_32 /* long from time.h or types.h */ +#define ACCUM_PMTYPE PM_TYPE_U64 /* accum_t from types.h */ +#endif /* _MIPS_SZLONG == 32 */ + +#if (_MIPS_SZLONG == 64) +#define IO_PMTYPE PM_TYPE_U64 /* ulong_t from procfs.h/prusage */ +#define CTX_PMTYPE PM_TYPE_U64 /* ulong_t from procfs.h/prusage */ +#define SYSCALLS_PMTYPE PM_TYPE_U64 /* ulong_t from procfs.h/prusage */ +#define SYSIDLE_PMTYPE PM_TYPE_64 /* long=time_t from sysinfo.h/sysinfo */ +#define TIME_PMTYPE PM_TYPE_64 /* long from time.h or types.h */ +#define ACCUM_PMTYPE PM_TYPE_U64 /* accum_t from types.h */ +#endif /* _MIPS_SZLONG == 64 */ + +extern char *conf_buffer; +extern char *conf_buffer_ptr; +extern char *pred_buffer; +char *configfile = NULL; +static int conf_gen = 1; /* configuration file generation number */ + +static int allow_stores = 1; /* allow stores or not */ + +static int hotproc_domain = 7; /* set in hotproc_init */ +static int pred_testing; /* just do predicate testing or not */ +static int parse_only; /* just parse config file and exit */ +static char* testing_fname; /* filename root for testing */ +static char *username; /* user account for pmda */ + +/* handle on /proc */ +static DIR *procdir; + +/* active list */ +static pid_t *active_list = NULL; /* generated per refresh */ +static int numactive = 0; +static int maxactive = INIT_PROC_MAX; + +/* process lists for current and previous */ +static process_t *proc_list[2] = {NULL, NULL}; + +/* array size allocated */ +static int maxprocs[2] = {INIT_PROC_MAX, INIT_PROC_MAX}; + +/* number of procs used in list (<= maxprocs) */ +static int numprocs[2] = {0, 0}; + +/* index into proc_list etc.. */ +static int current = 0; +static int previous = 1; + +/* refresh time interval in seconds - cmd arg */ +static struct timeval refresh_delta; + +/* event id for refreshing */ +static int refresh_afid; + +/* various cpu time totals */ +static int num_cpus = 0; +static int have_totals = 0; +static double transient; +static double cpuidle; +static double total_active; +static double total_inactive; + +/* number of refreshes */ +/* will wrap back to 2 */ +static unsigned long refresh_count = 0; + +/* format of an entry in /proc */ +char proc_fmt[8]; /* export for procfs fname conversions */ +int proc_entry_len; + +char *log = NULL; + +#ifndef USEC_PER_SEC +#define USEC_PER_SEC 1000000 /* number of usecs for 1 second */ +#endif +#ifndef NSEC_PER_SEC +#define NSEC_PER_SEC 1000000000 /* number of nsecs for 1 second */ +#endif + +#define ntime2double(x) \ + (((double)(x).tv_sec) + ((double)((ulong_t)((x).tv_nsec)))/NSEC_PER_SEC) + +#define utime2double(x) \ + (((double)(x).tv_sec) + ((double)((ulong_t)((x).tv_usec)))/USEC_PER_SEC) + +#define TWOe32 (double) 4.295e9 + + +/* + * Make initial allocation for proc list. + */ + +static int +init_proc_list(void) +{ + active_list = (pid_t*)malloc(INIT_PROC_MAX * sizeof(pid_t)); + proc_list[0] = (process_t*)malloc(INIT_PROC_MAX * sizeof(process_t)); + proc_list[1] = (process_t*)malloc(INIT_PROC_MAX * sizeof(process_t)); + if (proc_list[0] == NULL || proc_list[1] == NULL || active_list == NULL) + return -oserror(); + return 0; +} + +/* + * Work out the active list from the constraints. + */ + + +static void +init_active_list(void) +{ + numactive = 0; +} + +/* + * add_active_list: + * If unsuccessful in add - due to memory then return neg status. + * If member of active list return 1 + * If non-member of active list return 0 + */ + +static int +add_active_list(process_t *node, config_vars *vars) +{ + if (eval_tree(vars) == 0) { + return 0; + } + + if (numactive == maxactive) { + pid_t *res; + maxactive = numactive*2; + res = (pid_t *)realloc(active_list, maxactive * sizeof(pid_t)); + if (res == NULL) + return -osoerror(); + active_list = res; + } + active_list[numactive++] = node->pid; + return 1; +} + +#ifdef PCP_DEBUG + +static void +dump_active_list(void) +{ + int i = 0; + + (void)fprintf(stderr, "--- active list ---\n"); + for(i = 0; i < numactive; i++) { + (void)fprintf(stderr, "[%d] = %" FMT_PID "\n", i, active_list[i]); + }/*for*/ + (void)fprintf(stderr, "--- end active list ---\n"); +} + + +static void +dump_proc_list(void) +{ + int i; + process_t *node; + + (void)fprintf(stderr, "--- proc list ---\n"); + for (i = 0; i < numprocs[current]; i++) { + node = &proc_list[current][i]; + (void)fprintf(stderr, "[%d] = %" FMT_PID " ", i, node->pid); + (void)fprintf(stderr, "(syscalls = %ld) ", node->r_syscalls); + (void)fputc('\n', stderr); + }/*for*/ + (void)fprintf(stderr, "--- end proc list ---\n"); +} + +static void +dump_cputime(double pre_usr, double pre_sys, double post_usr, double post_sys) +{ + fprintf(stderr, "CPU Time: user = %f, sys = %f\n", + post_usr - pre_usr, post_sys - pre_sys); +} + +static void +dump_pred(derived_pred_t *pred) +{ + (void)fprintf(stderr, "--- pred vars ---\n"); + (void)fprintf(stderr, "syscalls = %f\n", pred->syscalls); + (void)fprintf(stderr, "ctxswitch = %f\n", pred->ctxswitch); + (void)fprintf(stderr, "virtualsize = %f\n", pred->virtualsize); + (void)fprintf(stderr, "residentsize = %f\n", pred->residentsize); + (void)fprintf(stderr, "iodemand = %f\n", pred->iodemand); + (void)fprintf(stderr, "iowait = %f\n", pred->iowait); + (void)fprintf(stderr, "schedwait = %f\n", pred->schedwait); + (void)fprintf(stderr, "--- end pred vars ---\n"); +} + +#endif + +/* + * Return 1 if pid is in active list. + * Return 0 if pid is NOT in active list. + */ + +static int +in_active_list(pid_t pid) +{ + int i; + + for(i = 0; i < numactive; i++) { + if (pid == active_list[i]) + return 1; + } + + return 0; +} + +static int +compar_pids(const void *n1, const void *n2) +{ + return ((process_t*)n2)->pid - ((process_t*)n1)->pid; +} + + +static void +set_proc_fmt(void) +{ + struct dirent *directp; /* go thru /proc directory */ + + for (rewinddir(procdir); directp=readdir(procdir);) { + if (!isdigit((int)directp->d_name[0])) + continue; + proc_entry_len = (int)strlen(directp->d_name); + (void)sprintf(proc_fmt, "%%0%dd", proc_entry_len); + break; + } +} + +/* + * look up node in proc list + */ +static process_t * +lookup_node(int curr_prev, pid_t pid) +{ + process_t key; + process_t *node; + + key.pid = pid; + + if ( (numprocs[curr_prev] > 0) && + ((node = bsearch(&key, proc_list[curr_prev], numprocs[curr_prev], + sizeof(process_t), compar_pids)) != NULL) ) { + return node; + } + return NULL; +} + +/* + * look up current node + */ +process_t * +lookup_curr_node(pid_t pid) +{ + return lookup_node(current, pid); +} + +/* + * Calculate difference allowing for single wrapping of counters + */ + +static double +DiffCounter(double current, double previous, int pmtype) +{ + double outval = current-previous; + + if (outval < 0.0) { + switch (pmtype) { + case PM_TYPE_32: + case PM_TYPE_U32: + outval += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + outval += (double)ULONGLONG_MAX+1; + break; + } + } + + return outval; +} + +static void +set_psinfo(prpsinfo_t *psinfo, config_vars *vars) +{ + psinfo->pr_size = (long)vars->pr_size; + psinfo->pr_rssize = (long)vars->pr_rssize; +} + +static void +set_psusage(prusage_t *psusage, config_vars *vars) +{ + psusage->pu_sysc = vars->pu_sysc; + psusage->pu_vctx = vars->pu_vctx; + psusage->pu_ictx = vars->pu_ictx; + psusage->pu_gbread = vars->pu_gbread; + psusage->pu_bread = vars->pu_bread; + psusage->pu_gbwrit = vars->pu_gbwrit; + psusage->pu_bwrit = vars->pu_bwrit; +} + +/* If have a test file then read in values + * and overwrite some variables in the proc buffer. + * Only read from file for each refresh => + * make each process have same line from file as values + * If read fails, then return -1 + * else if read something then return 0 + * else if not time to read then return 1 + */ + +static int +read_buf_file(int *rcount, char *suffix, FILE **testing_file, config_vars *vars) +{ + if (*testing_file == NULL) { + char fname[80]; + strcpy(fname, testing_fname); + strcat(fname, suffix); + *testing_file = fopen(fname, "r"); + if (*testing_file == NULL) { + fprintf(stderr, "%s: Unable to open test file \"%s\": %s\n", + pmProgname, fname, osstrerror()); + return -1; + } + } + /* only read new values for each refresh */ + if (refresh_count != *rcount) { + *rcount = (int)refresh_count; + if (read_test_values(*testing_file, vars) != 1) + return -1; + return 0; + } + + return 1; /* no values actually read */ +} + +static int +psusage_getbuf_file(pid_t pid, prusage_t *psusage) +{ + static config_vars vars; /* configuration variable values */ + static FILE *testing_file = NULL; + static int rcount = -1; /* last refresh_count */ + static int dont_read = 0; + int sts; + + if ((sts = psusage_getbuf(pid, psusage)) != 0) + return sts; + + if (testing_fname != NULL && !dont_read) { + sts = read_buf_file(&rcount, ".psusage", &testing_file, &vars); + if (sts >= 0) + set_psusage(psusage, &vars); + else + dont_read = 1; + } + return 0; +} + +static int +psinfo_getbuf_file(pid_t pid, prpsinfo_t *psinfo) +{ + static config_vars vars; /* configuration variable values */ + static FILE *testing_file = NULL; + static int rcount = -1; /* last refresh_count */ + static int dont_read = 0; + int sts; + + if ((sts = psinfo_getbuf(pid, psinfo)) != 0) + return sts; + + if (testing_fname != NULL && !dont_read) { + sts = read_buf_file(&rcount, ".psinfo", &testing_file, &vars); + if (sts >= 0) + set_psinfo(psinfo, &vars); + else + dont_read = 1; + } + return 0; +} + +static void +set_pracinfo(pracinfo_t *acct, config_vars *vars) +{ + acct->pr_timers.ac_bwtime = vars->ac_bwtime; + acct->pr_timers.ac_rwtime = vars->ac_rwtime; + acct->pr_timers.ac_qwtime = vars->ac_qwtime; +} + +static int +pracinfo_getbuf_file(pid_t pid, pracinfo_t *acct) +{ + static config_vars vars; /* configuration variable values */ + static FILE *testing_file = NULL; + static int rcount = -1; /* last refresh_count */ + static int dont_read = 0; + int sts; + + if ((sts = pracinfo_getbuf(pid, acct)) != 0) + return sts; + + if (testing_fname != NULL && !dont_read) { + sts = read_buf_file(&rcount, ".pracinfo", &testing_file, &vars); + if (sts >= 0) + set_pracinfo(acct, &vars); + else + dont_read = 1; + } + return 0; +} + +/* + * Refresh the process list for /proc entries. + */ + +static int +refresh_proc_list(void) +{ + extern int need_psusage; /* is psusage buffer needed or not */ + extern int need_accounting; /* is pracinfo buffer needed or not */ + + int sts; + int sysmp_sts; + pid_t pid; + struct dirent *directp; /* go thru /proc directory */ + prpsinfo_t psinfo; /* read in proc info */ + prusage_t psusage; /* read in proc usage */ + pracinfo_t acct; /* read in acct info */ + struct sysinfo sinfo; /* sysinfo from sysmp */ + process_t *oldnode = NULL; /* node from previous proc list */ + process_t *newnode = NULL; /* new node in current proc list */ + int np = 0; /* number of procs index */ + struct timeval p_timestamp; /* timestamp pre getting proc info */ + config_vars vars; /* configuration variable values */ + + struct timeval ts; + static double refresh_time[2]; /* timestamp after refresh */ + static time_t sysidle[2]; /* sys idle from sysmp */ + double sysidle_delta; /* system idle delta time since last refresh */ + double actual_delta; /* actual delta time since last refresh */ + double transient_delta; /* calculated delta time of transient procs */ + double cputime_delta; /* delta cpu time for a process */ + double syscalls_delta; /* delta num of syscalls for a process */ + double vctx_delta; /* delta num of valid ctx switches for a process */ + double ictx_delta; /* delta num of invalid ctx switches for a process */ + double bread_delta; /* delta num of bytes read */ + double gbread_delta; /* delta num of gigabytes read */ + double bwrit_delta; /* delta num of bytes written */ + double gbwrit_delta; /* delta num of gigabytes written */ + double bwtime_delta; /* delta num of nanosesc for waiting for blocked io */ + double rwtime_delta; /* delta num of nanosesc for waiting for raw io */ + double qwtime_delta; /* delta num of nanosesc waiting on run queue */ + double timestamp_delta; /* real time delta b/w refreshes for process */ + double total_cputime = 0; /* total of cputime_deltas for each process */ + double total_activetime = 0; /* total of cputime_deltas for active processes */ + double total_inactivetime = 0; /* total of cputime_deltas for inactive processes */ + +#ifdef PCP_DEBUG + double curr_time; /* for current time */ + double pre_usr, pre_sys; + double post_usr, post_sys; +#endif + + /* switch current and previous */ + if (current == 0) { + current = 1; previous = 0; + } + else { + current = 0; previous = 1; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmtimevalNow(&ts); + curr_time = utime2double(ts); + fprintf(stderr, "refresh_proc_list():\n"); + __pmProcessRunTimes(&pre_usr, &pre_sys); + } +#endif + +#ifdef PCP_DEBUG + /* used to verify externally (approx.) the values */ + /* note: doing this is _slow_ */ + if (pmDebug & DBG_TRACE_APPL2) { + static char cmd[80]; + sprintf(cmd, "(date ; ps -le )>> %s.refresh", log); + fprintf(stderr, "refresh: cmd = %s\n", cmd); + system(cmd); + } +#endif + + init_active_list(); + + (void)memset(&vars, 0, sizeof(config_vars)); + + for (np = 0, rewinddir(procdir); directp=readdir(procdir);) { + if (!isdigit((int)directp->d_name[0])) + continue; + (void)sscanf(directp->d_name, proc_fmt, &pid); + + __pmtimevalNow(&p_timestamp); + + if (psinfo_getbuf_file(pid, &psinfo) != 0) + continue; + + /* reallocate if run out of room */ + if (np == maxprocs[current]) { + process_t *res; + maxprocs[current] = np*2; + res = (process_t *)realloc(proc_list[current], + maxprocs[current] * sizeof(process_t)); + if (res == NULL) + return -oserror(); + proc_list[current] = res; + } + + newnode = &proc_list[current][np++]; + newnode->pid = pid; + newnode->r_cputime = ntime2double(psinfo.pr_time); + newnode->r_cputimestamp = utime2double(p_timestamp); + + if (need_psusage) { + if (psusage_getbuf_file(pid, &psusage) != 0) + continue; + newnode->r_syscalls = psusage.pu_sysc; + newnode->r_vctx = psusage.pu_vctx; + newnode->r_ictx = psusage.pu_ictx; + newnode->r_bread = psusage.pu_bread; + newnode->r_gbread = psusage.pu_gbread; + newnode->r_bwrit = psusage.pu_bwrit; + newnode->r_gbwrit = psusage.pu_gbwrit; + } + + if (need_accounting) { + if (pracinfo_getbuf_file(pid, &acct) != 0) + continue; + newnode->r_bwtime = acct.pr_timers.ac_bwtime; + newnode->r_rwtime = acct.pr_timers.ac_rwtime; + newnode->r_qwtime = acct.pr_timers.ac_qwtime; + } + + /* if we have a previous i.e. not the first time */ + if ((oldnode = lookup_node(previous, pid)) != NULL) { + + + cputime_delta = DiffCounter(newnode->r_cputime, oldnode->r_cputime, TIME_PMTYPE); + timestamp_delta = DiffCounter(newnode->r_cputimestamp, oldnode->r_cputimestamp, TIME_PMTYPE); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: " + "fname = \"%s\"; cputime = %f; elapsed = %f\n", + psinfo.pr_fname, cputime_delta, timestamp_delta); + } +#endif +#ifdef PCP_DEBUG + /* used to verify externally (approx.) the values */ + /* note: doing this is _slow_ */ + if (pmDebug & DBG_TRACE_APPL2) { + static char cmd[80]; + if (cputime_delta > timestamp_delta) { + sprintf(cmd, "(date ; ps -lp %" FMT_PID " )>> %s.refresh", pid, log); + fprintf(stderr, "refresh: cmd = %s\n", cmd); + system(cmd); + } + } +#endif + newnode->r_cpuburn = cputime_delta / timestamp_delta; + vars.cpuburn = newnode->r_cpuburn; + + if (need_psusage) { + + /* rate convert syscalls */ + syscalls_delta = DiffCounter((double)newnode->r_syscalls, + (double)oldnode->r_syscalls, SYSCALLS_PMTYPE); + vars.preds.syscalls = syscalls_delta / timestamp_delta; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: " + "syscalls = %f\n", vars.preds.syscalls); + } +#endif + + /* rate convert ctxswitch */ + vctx_delta = DiffCounter((double)newnode->r_vctx, + (double)oldnode->r_vctx, CTX_PMTYPE); + ictx_delta = DiffCounter((double)newnode->r_ictx, + (double)oldnode->r_ictx, CTX_PMTYPE); + vars.preds.ctxswitch = (vctx_delta + ictx_delta) / timestamp_delta; + + /* rate convert reading/writing (iodemand) */ + bread_delta = DiffCounter((double)newnode->r_bread, + (double)oldnode->r_bread, IO_PMTYPE); + gbread_delta = DiffCounter((double)newnode->r_gbread, + (double)oldnode->r_gbread, IO_PMTYPE); + bwrit_delta = DiffCounter((double)newnode->r_bwrit, + (double)oldnode->r_bwrit, IO_PMTYPE); + gbwrit_delta = DiffCounter((double)newnode->r_gbwrit, + (double)oldnode->r_gbwrit, IO_PMTYPE); + vars.preds.iodemand = (gbread_delta * 1024 * 1024 + + (double)bread_delta / 1024 + + gbwrit_delta * 1024 * 1024 + + (double)bwrit_delta / 1024) / + timestamp_delta; + + } + + if (need_accounting) { + + /* rate convert iowait */ + bwtime_delta = DiffCounter((double)newnode->r_bwtime, + (double)oldnode->r_bwtime, ACCUM_PMTYPE); + bwtime_delta /= 1000000000.0; /* nanosecs -> secs */ + rwtime_delta = DiffCounter((double)newnode->r_rwtime, + (double)oldnode->r_rwtime, ACCUM_PMTYPE); + rwtime_delta /= 1000000000.0; /* nanosecs -> secs */ + vars.preds.iowait = (bwtime_delta + rwtime_delta) / timestamp_delta; + + + /* rate convert schedwait */ + qwtime_delta = DiffCounter((double)newnode->r_qwtime, + (double)oldnode->r_qwtime, ACCUM_PMTYPE); + qwtime_delta /= 1000000000.0; /* nanosecs -> secs */ + vars.preds.schedwait = qwtime_delta / timestamp_delta; + + } + + newnode->preds = vars.preds; /* struct copy */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + dump_pred(&newnode->preds); + } +#endif + + } + else { + newnode->r_cpuburn = 0; + bzero(&newnode->preds, sizeof(newnode->preds)); + vars.cpuburn = 0; + vars.preds.syscalls = 0; + vars.preds.ctxswitch = 0; + vars.preds.iowait = 0; + vars.preds.schedwait = 0; + vars.preds.iodemand = 0; + cputime_delta = 0; + } + + total_cputime += cputime_delta; + + /* fix up vars record from psinfo */ + (void)strcpy(vars.fname, psinfo.pr_fname); + (void)strcpy(vars.psargs, psinfo.pr_psargs); + vars.uid = psinfo.pr_uid; + vars.gid = psinfo.pr_gid; + vars.uname = NULL; + vars.gname = NULL; + vars.preds.virtualsize = (double)psinfo.pr_size * (_pagesize / 1024); + vars.preds.residentsize = (double)psinfo.pr_rssize * (_pagesize / 1024); + + if ((sts = add_active_list(newnode, &vars)) < 0) { + return sts; + } + + if (sts == 0) + total_inactivetime += cputime_delta; + else + total_activetime += cputime_delta; + + }/*for each pid*/ + + numprocs[current] = np; + + __pmtimevalNow(&ts); + refresh_time[current] = utime2double(ts); + + refresh_count++; + /* If we wrap then say that we atleast have 2, + * inorder to indicate we have seen two successive refreshes. + */ + if (refresh_count == 0) + refresh_count = 2; + + bzero(&sinfo, sizeof(sinfo)); /* for purify */ + if ((sysmp_sts = (int)sysmp(MP_SAGET, MPSA_SINFO, &sinfo, sizeof(struct sysinfo))) < 0) { + __pmNotifyErr(LOG_ERR, "sysmp failed in refresh: %s\n", osstrerror()); + sysidle[current] = -1; + } + else { + sysidle[current] = sinfo.cpu[CPU_IDLE]; + } + + + have_totals = 0; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: refresh_count = %lu\n", + refresh_count); + } +#endif + + if (refresh_count > 1 && sysmp_sts != -1 && sysidle[previous] != -1) { + sysidle_delta = DiffCounter(sysidle[current], sysidle[previous], SYSIDLE_PMTYPE) / (double)HZ; + actual_delta = DiffCounter(refresh_time[current], refresh_time[previous], TIME_PMTYPE); + transient_delta = num_cpus * actual_delta - (total_cputime + sysidle_delta); + if (transient_delta < 0) /* sysidle is only accurate to 0.01 second */ + transient_delta = 0; + + have_totals = 1; + transient = transient_delta / actual_delta; + cpuidle = sysidle_delta / actual_delta; + total_active = total_activetime / actual_delta; + total_inactive = total_inactivetime / actual_delta; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: " + "total_cputime = %f\n", total_cputime); + (void)fprintf(stderr, "refresh_proc_list: " + "total_activetime = %f, total_inactivetime = %f\n", + total_activetime, total_inactivetime); + (void)fprintf(stderr, "refresh_proc_list: " + "sysidle_delta = %f, actual_delta = %f\n", + sysidle_delta, actual_delta); + (void)fprintf(stderr, "refresh_proc_list: " + "transient_delta = %f, transient = %f\n", + transient_delta, transient); + } +#endif + } + + + /* sort it for bsearching later */ + qsort(proc_list[current], numprocs[current], + sizeof(process_t), compar_pids); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmProcessRunTimes(&post_usr, &post_sys); + dump_proc_list(); + fprintf(stderr, "refresh_proc_list: duration = %f secs; ", + refresh_time[current] - curr_time); + dump_cputime(pre_usr, pre_sys, post_usr, post_sys); + dump_active_list(); + } +#endif + + return 0; +}/*refresh_proc_list*/ + + +static int +restart_proc_list(void) +{ + int sts; + + refresh_count = 0; + numprocs[current] = 0; /* clear the current list */ + sts = refresh_proc_list(); + + return sts; +} + +static void +timer_callback(int afid, void *data) +{ + int sts = refresh_proc_list(); + + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "timer_callback: refresh list failed: %s\n", pmErrStr(sts)); + } +} + + +static int +hotproc_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + __pmInResult *res; + int j; + int sts; + + + if (indom != proc_indom) + return PM_ERR_INDOM; + + if ((res = (__pmInResult *)malloc(sizeof(*res))) == NULL) + return -oserror(); + res->indom = indom; + res->instlist = NULL; + res->namelist = NULL; + sts = 0; + + if (name == NULL && inst == PM_IN_NULL) + res->numinst = numactive; + else + res->numinst = 1; + + if (inst == PM_IN_NULL) { + if ((res->instlist = (int *)malloc(res->numinst * sizeof(res->instlist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + } + + if (name == NULL) { + if ((res->namelist = (char **)malloc(res->numinst * sizeof(res->namelist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + for (j = 0; j < res->numinst; j++) + res->namelist[j] = NULL; + } + + /* --> names and ids (the whole instance map) */ + if (name == NULL && inst == PM_IN_NULL) { + /* find all instance ids and names for the PROC indom */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + (void)fprintf(stderr, "hotproc_instance: Getting whole instance map...\n"); + } +#endif + + /* go thru active list */ + for(j = 0; j < numactive; j++) { + res->instlist[j] = active_list[j]; + if ((sts = proc_id_to_mapname(active_list[j], &res->namelist[j])) < 0) + break; + } + } + /* id --> name */ + else if (name == NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + (void)fprintf(stderr, "hotproc_instance: id --> name\n"); + } +#endif + if (!in_active_list(inst)) { + __pmNotifyErr(LOG_ERR, "proc_instance: pid not in active list %d\n", inst); + sts = PM_ERR_INST; + } + else { + sts = proc_id_to_name(inst, &res->namelist[0]); + } + } + /* name --> id */ + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + (void)fprintf(stderr, "hotproc_instance: name --> id\n"); + } +#endif + /* find the instance for the given indom/name */ + sts = proc_name_to_id(name, &res->instlist[0]); + if (!in_active_list(res->instlist[0])) { + __pmNotifyErr(LOG_ERR, "proc_instance: pid not in active list %d\n", + res->instlist[0]); + sts = PM_ERR_INST; + } + } + + if (sts == 0) + *result = res; + else + __pmFreeInResult(res); + + return sts; +} + +static int +hotproc_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda) +{ + int pmidErr; + int ctl_i; + __pmID_int *pmidp = (__pmID_int *)&pmid; + + pmidErr = PM_ERR_PMID; + if (pmidp->domain == hotproc_domain) { + ctl_i = lookup_ctltab(pmidp->cluster); + if (ctl_i < 0) + pmidErr = ctl_i; + else + pmidErr = ctltab[ctl_i].getdesc(pmid, desc); + } + + return pmidErr; + +} + + +static int +hotproc_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; /* over pmidlist[] */ + int ctl_i; /* over ctltab[] and fetched[] */ + int j; + int n; + int numvals; + int sts; + size_t need; + static pmResult *res = NULL; + static int maxnpmids = 0; + pmValueSet *vset; + pmDesc dp; + __pmID_int *pmidp; + pmAtomValue atom; + int aggregate_len; + static int max_numactive = 0; + static int **fetched = NULL; + + if (fetched == NULL) { + fetched = (int **)malloc(nctltab * sizeof(fetched[0])); + if (fetched == NULL) + return -oserror(); + memset(fetched, 0, nctltab * sizeof(fetched[0])); + } + + /* allocate for result structure */ + if (numpmid > maxnpmids) { + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = sizeof(pmResult) + (numpmid - 1) * sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == NULL) + return -oserror(); + maxnpmids = numpmid; + } + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + + /* fix up allocations for fetched array */ + for (ctl_i=1; ctl_i < nctltab; ctl_i++) { + if (numactive > max_numactive) { + int *f = (int*)realloc(fetched[ctl_i], numactive * sizeof(int)); + int ctl_j; /* over ctltab[] and fetched[] */ + if (f == NULL) { + max_numactive = 0; + for (ctl_j=1; ctl_j < nctltab; ctl_j++) { + if (fetched[ctl_j]) + free(fetched[ctl_j]); + fetched[ctl_j] = NULL; + } + return -oserror(); + } + fetched[ctl_i] = f; + } + (void)memset(fetched[ctl_i], 0, numactive * sizeof(int)); + if ((sts = ctltab[ctl_i].allocbuf(numactive)) < 0) + return sts; + }/*for*/ + if (numactive > max_numactive) { + max_numactive = numactive; + } + + sts = 0; + for (i = 0; i < numpmid; i++) { + int pmidErr = 0; + + pmidp = (__pmID_int *)&pmidlist[i]; + pmidErr = hotproc_desc(pmidlist[i], &dp, pmda); + + /* create a vset with the error code in it */ + if (pmidErr < 0) { + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)); + if (vset == NULL) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = pmidErr; + continue; + } + + if (pmidp->cluster == CLUSTER_GLOBAL) { + + /* global metrics, singular instance domain */ + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)); + if (vset == NULL) { + if (i > 0) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = 1; + + switch (pmidp->item) { + case ITEM_NPROCS: + { + char *path; + + /* pv#589180 + * Reduce the active list if processes have exitted. + */ + for (j=0; j < numactive; j++) { + pid_t pid = active_list[j]; + proc_pid_to_path(pid, NULL, &path, PINFO_PATH); + /* if process not found then remove from active list */ + if (access(path, R_OK) < 0) { + int ctl_k; + /* remove from active list */ + /* replace with end one */ + active_list[j] = active_list[numactive-1]; + /* also do same thing to fetched array */ + for(ctl_k = 1; ctl_k < nctltab; ctl_k++) { + fetched[ctl_k][j] = fetched[ctl_k][numactive-1]; + } + numactive--; + j--; /* test this slot again */ + } + } + + atom.l = numactive; + break; + } + case ITEM_REFRESH: + atom.l = (__int32_t)refresh_delta.tv_sec; + break; + case ITEM_CONFIG: + atom.cp = pred_buffer; + break; + case ITEM_CONFIG_GEN: + atom.l = conf_gen; + break; + case ITEM_TRANSIENT: + if (!have_totals) + vset->numval = 0; + else + atom.f = transient; + break; + case ITEM_CPUIDLE: + if (!have_totals) + vset->numval = 0; + else + atom.f = cpuidle; + break; + case ITEM_TOTAL_CPUBURN: + if (!have_totals) + vset->numval = 0; + else + atom.f = total_active; + break; + case ITEM_TOTAL_NOTCPUBURN: + if (!have_totals) + vset->numval = 0; + else + atom.f = total_inactive; + break; + case ITEM_OTHER_TOTAL: + if (!have_totals) + vset->numval = 0; + else + atom.f = transient + total_inactive; + break; + case ITEM_OTHER_PERCENT: + { + double other = transient + total_inactive; + double non_idle = other + total_active; + + /* if non_idle = 0, very unlikely, + * then the value here is meaningless + */ + if (!have_totals || non_idle == 0) + vset->numval = 0; + else + atom.f = other / non_idle * 100; + } + break; + } + + if (vset->numval == 1 ) { + sts = __pmStuffValue(&atom, 0, &vset->vlist[0], dp.type); + vset->valfmt = sts; + vset->vlist[0].inst = PM_IN_NULL; + } + } + else { + /* + * Multiple instance domain metrics. + */ + if ((ctl_i = lookup_ctltab(pmidp->cluster)) < 0) + return ctl_i; + if (!ctltab[ctl_i].supported) { + numvals = 0; + } + else if (pmidp->cluster == CLUSTER_PRED && !ppred_available(pmidp->item)) { + numvals = 0; + } + else { + pid_t pid; + + numvals = 0; + for (j = 0; j < numactive; j++) { + pid = active_list[j]; + if (!__pmInProfile(proc_indom, pmda->e_prof, pid)) + continue; + if (fetched[ctl_i][j] == 0) { + int sts; + if ((sts = ctltab[ctl_i].getinfo(pid, j)) == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "hotproc_fetch: getinfo succeeded: " + "pid=%" FMT_PID ", j=%d\n", pid, j); + } +#endif + fetched[ctl_i][j] = 1; + numvals++; + } + else if (sts == -ENOENT) { + int ctl_k; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "hotproc_fetch: " + "getinfo failed: pid=%" FMT_PID ", j=%d\n", pid, j); + } +#endif + /* remove from active list */ + /* replace with end one */ + active_list[j] = active_list[numactive-1]; + /* also do same thing to fetched array */ + for(ctl_k = 1; ctl_k < nctltab; ctl_k++) { + fetched[ctl_k][j] = fetched[ctl_k][numactive-1]; + } + numactive--; + j--; /* test this slot again */ + } + } + else { + numvals++; + } + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "hotproc_fetch: numvals = %d\n", numvals); + } +#endif + + res->vset[i] = vset = (pmValueSet *)malloc( + sizeof(pmValueSet) + (numvals-1) * sizeof(pmValue)); + if (vset == NULL) { + if (i > 0) { /* not the first malloc failing */ + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = numvals; + vset->valfmt = PM_VAL_INSITU; + + if (!ctltab[ctl_i].supported) { + vset->numval = PM_ERR_APPVERSION; + } + else { + if (numvals != 0) { + pid_t pid; + for (n=j=0; j < numactive; j++) { + pid = active_list[j]; + if (fetched[ctl_i][j] == 0) + continue; + + aggregate_len = ctltab[ctl_i].setatom(pmidp->item, &atom, j); + sts = __pmStuffValue(&atom, aggregate_len, &vset->vlist[n], dp.type); + vset->valfmt = sts; + vset->vlist[n].inst = pid; + n++; + }/*for*/ + }/*if*/ + }/*if*/ + + }/*if*/ + }/*for*/ + + *resp = res; + return 0; +}/*hotproc_fetch*/ + +static int +restart_refresh(void) +{ + int sts; + + /* + * Reschedule as if starting from init. + */ + sts = restart_proc_list(); + if (sts >= 0) { + /* Get rid of current event from queue, register new one */ + __pmAFunregister(refresh_afid); + refresh_afid = __pmAFregister(&refresh_delta, NULL, timer_callback); + } + + return sts; +} + +static int +hotproc_store(pmResult *result, pmdaExt *pmda) +{ + int i; + pmValueSet *vsp; + pmDesc desc; + __pmID_int *pmidp; + int sts = 0; + pmAtomValue av; + + if (!allow_stores) { + return PM_ERR_PERMISSION; + } + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + sts = hotproc_desc(vsp->pmid, &desc, pmda); + if (sts < 0) + break; + if (pmidp->cluster == CLUSTER_GLOBAL) { + switch (pmidp->item) { + case ITEM_REFRESH: + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_INSITU) { + sts = PM_ERR_CONV; + } + break; + case ITEM_CONFIG: + if (vsp->numval != 1 || vsp->valfmt == PM_VAL_INSITU) { + sts = PM_ERR_CONV; + } + break; + default: + sts = PM_ERR_PERMISSION; + break; + } + } + else { + sts = PM_ERR_PERMISSION; + } + + if (sts < 0) + break; + + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + desc.type, &av, desc.type)) < 0) + break; + + switch (pmidp->item) { + case ITEM_REFRESH: + if (av.l < MIN_REFRESH) { + sts = PM_ERR_CONV; + } + else { + refresh_delta.tv_sec = av.l; + + if ((sts = restart_refresh()) < 0) + __pmNotifyErr(LOG_ERR, "hotproc_store: refresh list failed: %s\n", + pmErrStr(sts)); + } + break; + case ITEM_CONFIG: + { + bool_node *tree; + + conf_buffer_ptr = av.cp; + + /* Only accept the new config if its predicate is parsed ok */ + if (parse_config(&tree) != 0) { + conf_buffer_ptr = conf_buffer; + free(av.cp); + sts = PM_ERR_CONV; + } + else { + conf_gen++; + new_tree(tree); + free(conf_buffer); /* free old one */ + conf_buffer = av.cp; /* use the new one */ + if ((sts = restart_refresh()) < 0) + __pmNotifyErr(LOG_ERR, "hotproc_store: refresh list failed: %s\n", + pmErrStr(sts)); + } + } + break; + }/*switch*/ + + }/*for*/ + + return sts; +} + + + +/* + * Initialise the agent (only daemon and NOT DSO). + */ + +static void +hotproc_init(pmdaInterface *dp) +{ + int sts; + + __pmSetProcessIdentity(username); + + dp->version.two.fetch = hotproc_fetch; + dp->version.two.store = hotproc_store; + dp->version.two.desc = hotproc_desc; + dp->version.two.instance = hotproc_instance; + + /* + * Maintaining my own desc and indom table/info. + * This is why the passed in tables are NULL. + */ + pmdaInit(dp, NULL, 0, NULL, 0); + init_tables(dp->domain); + hotproc_domain = dp->domain; + + if ((procdir = opendir(PROCFS)) == NULL) { + dp->status = -oserror(); + __pmNotifyErr(LOG_ERR, "opendir(%s) failed: %s\n", PROCFS, osstrerror()); + return; + } + + set_proc_fmt(); + + + if ((num_cpus = (int)sysmp(MP_NPROCS)) < 0) { + dp->status = -oserror(); + __pmNotifyErr(LOG_ERR, "sysmp failed to get NPROCS: %s\n", osstrerror()); + return; + } + + if ((sts = init_proc_list()) < 0) { + dp->status = sts; + return; + } + + if ((sts = restart_proc_list()) < 0) { + dp->status = sts; + return; + } +} + + + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [options] configfile\n\n", pmProgname); + (void)fputs("Options:\n" + " -C parse configfile and exit\n" + " -U username user account to run under (default \"pcp\")\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -s do NOT allow dynamic changes to the selection predicate\n" + " -t refresh set the refresh time interval in seconds\n", + stderr); + exit(1); +} + + +static void +get_options(int argc, char *argv[], pmdaInterface *dispatch) +{ + int n; + int err = 0; + char *err_msg; + + + while ((n = pmdaGetOpt(argc, argv, "CD:d:l:t:U:xX:?s", + dispatch, &err)) != EOF) { + switch (n) { + + case 'C': + parse_only = 1; + break; + + case 's': + allow_stores = 0; + break; + + case 't': + if (pmParseInterval(optarg, &refresh_delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -t requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + break; + + case 'U': + username = optarg; + break; + + case 'x': + /* hidden option for predicate testing */ + pred_testing = 1; + break; + + case 'X': + /* hidden option for predicate testing with iocntl buffers */ + testing_fname = optarg; + break; + + case '?': + err++; + } + } + + if (err || optind != argc -1) { + usage(); + } + + configfile = argv[optind]; +} + + + +/* + * Set up the agent for running as a daemon. + */ + +int +main(int argc, char **argv) +{ + pmdaInterface dispatch; + char *p; + int sep = __pmPathSeparator(); + int infd; + fd_set fds; + fd_set readyfds; + int nready; + int numfds; + FILE *conf; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + refresh_delta.tv_sec = 10; + refresh_delta.tv_usec = 0; + + snprintf(mypath, sizeof(mypath), "%s%c" "hotproc" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, HOTPROC, + "hotproc.log", mypath); + + get_options(argc, argv, &dispatch); + + if (pred_testing) { + do_pred_testing(); + exit(0); + } + + if (!parse_only) { + pmdaOpenLog(&dispatch); + log = dispatch.version.two.ext->e_logfile; + } + + conf = open_config(); + read_config(conf); + (void)fclose(conf); + + if (parse_only) + exit(0); + + hotproc_init(&dispatch); + pmdaConnect(&dispatch); + + if ((infd = __pmdaInFd(&dispatch)) < 0) + exit(1); + FD_ZERO(&fds); + FD_SET(infd, &fds); + numfds = infd+1; + + refresh_afid = __pmAFregister(&refresh_delta, NULL, timer_callback); + + /* custom pmda main loop */ + for (;;) { + (void)memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(numfds, &readyfds, NULL, NULL, NULL); + + if (nready > 0) { + __pmAFblock(); + if (__pmdaMainPDU(&dispatch) < 0) + break; + __pmAFunblock(); + } + else if (nready < 0 && neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failed: %s\n", netstrerror()); + } + } + + exit(0); +} diff --git a/src/pmdas/hotproc/src/hotproc.h b/src/pmdas/hotproc/src/hotproc.h new file mode 100644 index 0000000..1c68fea --- /dev/null +++ b/src/pmdas/hotproc/src/hotproc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PHOTPROC_H +#define PHOTPROC_H + +#include "config.h" + +/* main process node type */ +typedef struct process_t { + pid_t pid; + + /* refreshed data */ + ulong_t r_vctx; + ulong_t r_ictx; + ulong_t r_syscalls; + ulong_t r_bread; + ulong_t r_gbread; + ulong_t r_bwrit; + ulong_t r_gbwrit; + + float r_cpuburn; + double r_cputimestamp; + double r_cputime; + + accum_t r_bwtime; + accum_t r_rwtime; + accum_t r_qwtime; + + /* predicate values */ + derived_pred_t preds; + +} process_t; + +process_t * lookup_curr_node(pid_t); + +#endif diff --git a/src/pmdas/hotproc/src/lex.l b/src/pmdas/hotproc/src/lex.l new file mode 100644 index 0000000..d19f999 --- /dev/null +++ b/src/pmdas/hotproc/src/lex.l @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +%{ +#include "./gram_node.h" +#include "./gram.h" + +void yyerror(char *s); + +static char emsg[256]; /* error message */ +static char yy_ichar; /* input char from conf_buffer */ +extern char *conf_buffer_ptr; + +#undef input +#undef unput +#define input() ( (yy_ichar = *conf_buffer_ptr++) == '\n' ? (yylineno++, yy_ichar) : yy_ichar) +#define unput(c) { if ((*--conf_buffer_ptr = c) == '\n') yylineno--; } + +%} + +%option noinput +%option nounput + +%% + +Version { return VERSION; } +schedwait { return SCHEDWAIT; } +iowait { return IOWAIT; } +iodemand { return IODEMAND; } +residentsize { return RESIDENTSIZE; } +virtualsize { return VIRTUALSIZE; } +ctxswitch { return CTXSWITCH; } +syscalls { return SYSCALLS; } +gid { return GID; } +uid { return UID; } +uname { return UNAME; } +gname { return GNAME; } +fname { return FNAME; } +psargs { return PSARGS; } +cpuburn { return CPUBURN; } +"&&" { return AND; } +"||" { return OR; } +"!" { return NOT; } +"(" { return LPAREN; } +")" { return RPAREN; } +true { return TRUE; } +false { return FALSE; } +"==" { return EQUAL; } +"!=" { return NEQUAL; } +"<" { return LTHAN; } +"<=" { return LEQUAL; } +">" { return GTHAN; } +">=" { return GEQUAL; } +"~" { return MATCH; } +"!~" { return NMATCH; } + +\/[^/\n]*[/\n] { + char *str; + yylval.y_str = (char *)malloc(yyleng-1); + if (yylval.y_str == 0) { + (void)sprintf(emsg, "malloc failed: %s", osstrerror()); + yyerror(emsg); + } + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + if ((str = re_comp(yylval.y_str)) != 0) { + yyerror(str); + } + return PATTERN; + } + +\"[^"\n]*["\n] { + yylval.y_str = (char *)malloc(yyleng-1); + if (yylval.y_str == 0) { + (void)sprintf(emsg, "malloc failed: %s", osstrerror()); + yyerror(emsg); + } + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + return STRING; + } + + +[0-9]+ | +[0-9]*"."[0-9]+ | +[0-9]+"."[0-9]* { + yylval.y_number = atof(yytext); + return NUMBER; + } + +\#.*\n { } + +[\t \r\n]+ { } + + +[a-zA-Z]+ { + yyerror("Illegal word"); + } + +. { + yyerror("Illegal character"); + } +%% + diff --git a/src/pmdas/hotproc/src/pcpu.c b/src/pmdas/hotproc/src/pcpu.c new file mode 100644 index 0000000..da6abbd --- /dev/null +++ b/src/pmdas/hotproc/src/pcpu.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "proc.h" +#include "proc_aux.h" +#include "cluster.h" +#include "pcpu.h" +#include "hotproc.h" + +static pmDesc desctab[] = { + /* hotproc.cpuburn */ + { PMID(CLUSTER_CPU,ITEM_CPUBURN), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + +}; + +static int ndesc = (sizeof(desctab)/sizeof(desctab[0])); + +static float *cpuburn_buf = NULL; + +void +pcpu_init(int dom) +{ + init_table(ndesc, desctab, dom); +} + +int +pcpu_getdesc(pmID pmid, pmDesc *desc) +{ + return getdesc(ndesc, desctab, pmid, desc); +} + +int +pcpu_setatom(int item, pmAtomValue *atom, int j) +{ + switch (item) { + case ITEM_CPUBURN: + atom->f = cpuburn_buf[j]; + break; + } + return 0; +} + +int +pcpu_getinfo(pid_t pid, int j) +{ + process_t *node; + char *path; + + node = lookup_curr_node(pid); + if (node == NULL) { + /* node should be there if it's in active list ! */ + (void)fprintf(stderr, "%s: Internal error for lookup_node()", pmProgname); + exit(1); + } + proc_pid_to_path(pid, NULL, &path, PINFO_PATH); + if (access(path, R_OK) < 0) + return -oserror(); + + cpuburn_buf[j] = node->r_cpuburn; + return 0; +} + + +int +pcpu_allocbuf(int size) +{ + static int max_size = 0; + float *cpub; + + if (size > max_size) { + cpub = realloc(cpuburn_buf, size * sizeof(float)); + if (cpub == NULL) + return -oserror(); + cpuburn_buf = cpub; + max_size = size; + } + + return 0; +} diff --git a/src/pmdas/hotproc/src/pcpu.h b/src/pmdas/hotproc/src/pcpu.h new file mode 100644 index 0000000..9bcf65c --- /dev/null +++ b/src/pmdas/hotproc/src/pcpu.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PCPU_H +#define PCPU_H + +#define CLUSTER_CPU 102 +#define ITEM_CPUBURN 0 + +void pcpu_init(int dom); +int pcpu_getdesc(pmID pmid, pmDesc *desc); +int pcpu_setatom(int item, pmAtomValue *atom, int j); +int pcpu_getinfo(pid_t pid, int j); +int pcpu_allocbuf(int size); + +#endif diff --git a/src/pmdas/hotproc/src/pglobal.c b/src/pmdas/hotproc/src/pglobal.c new file mode 100644 index 0000000..3c4a1ac --- /dev/null +++ b/src/pmdas/hotproc/src/pglobal.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmapi.h" + +#include "./proc.h" +#include "./proc_aux.h" +#include "./pglobal.h" + +static pmDesc desctab[] = { + { PMID(CLUSTER_GLOBAL,ITEM_NPROCS), + PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_REFRESH), + PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_CONFIG), + PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_CONFIG_GEN), + PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_CPUIDLE), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_TOTAL_CPUBURN), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_TRANSIENT), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_TOTAL_NOTCPUBURN), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_OTHER_TOTAL), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_OTHER_PERCENT), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} } +}; + +static int ndesc = (sizeof(desctab)/sizeof(desctab[0])); + + +void +pglobal_init(int dom) +{ + init_table(ndesc, desctab, dom); +} + +int +pglobal_getdesc(pmID pmid, pmDesc *desc) +{ + return getdesc(ndesc, desctab, pmid, desc); +} + +int +pglobal_setatom(int item, pmAtomValue *atom, int j) +{ + /* noop */ + return 0; +} + + +int +pglobal_getinfo(pid_t pid, int j) +{ + /* noop */ + return 0; +} + + +int +pglobal_allocbuf(int size) +{ + /* noop */ + return 0; +} diff --git a/src/pmdas/hotproc/src/pglobal.h b/src/pmdas/hotproc/src/pglobal.h new file mode 100644 index 0000000..659d7e0 --- /dev/null +++ b/src/pmdas/hotproc/src/pglobal.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PGLOBAL_H +#define PGLOBAL_H + +#define CLUSTER_GLOBAL 100 + +#define ITEM_NPROCS 0 +#define ITEM_REFRESH 1 +#define ITEM_CPUIDLE 2 +#define ITEM_TOTAL_CPUBURN 3 +#define ITEM_TRANSIENT 4 +#define ITEM_TOTAL_NOTCPUBURN 5 +#define ITEM_OTHER_TOTAL 6 +#define ITEM_OTHER_PERCENT 7 +#define ITEM_CONFIG 8 +#define ITEM_CONFIG_GEN 9 + +void pglobal_init(int dom); +int pglobal_getdesc(pmID pmid, pmDesc *desc); +int pglobal_setatom(int item, pmAtomValue *atom, int j); +int pglobal_getinfo(pid_t pid, int j); +int pglobal_allocbuf(int size); + +#endif diff --git a/src/pmdas/hotproc/src/ppred_values.c b/src/pmdas/hotproc/src/ppred_values.c new file mode 100644 index 0000000..98e17f1 --- /dev/null +++ b/src/pmdas/hotproc/src/ppred_values.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "proc.h" +#include "proc_aux.h" +#include "cluster.h" +#include "ppred_values.h" +#include "hotproc.h" + + +static pmDesc desctab[] = { + /* hotproc.predicate.syscalls */ + { PMID(CLUSTER_PRED,ITEM_SYSCALLS), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,-1,1, 0,PM_TIME_SEC,0} }, + /* hotproc.predicate.ctxswitch */ + { PMID(CLUSTER_PRED,ITEM_CTXSWITCH), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,-1,1, 0,PM_TIME_SEC,0} }, + /* hotproc.predicate.virtualsize */ + { PMID(CLUSTER_PRED,ITEM_VIRTUALSIZE), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {1,0,0, PM_SPACE_KBYTE,0,0} }, + /* hotproc.predicate.residentsize */ + { PMID(CLUSTER_PRED,ITEM_RESIDENTSIZE), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {1,0,0, PM_SPACE_KBYTE,0,0} }, + /* hotproc.predicate.iodemand */ + { PMID(CLUSTER_PRED,ITEM_IODEMAND), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {1,-1,0, PM_SPACE_KBYTE,PM_TIME_SEC,0} }, + + /* + * NOTE + * iowait and schedwait really have units of sec/sec, i.e. utilization + */ + /* hotproc.predicate.iowait */ + { PMID(CLUSTER_PRED,ITEM_IOWAIT), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,0,0, 0,0,0} }, + /* hotproc.predicate.schedwait */ + { PMID(CLUSTER_PRED,ITEM_SCHEDWAIT), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,0,0, 0,0,0} }, +}; + +static int ndesc = (sizeof(desctab)/sizeof(desctab[0])); + +static derived_pred_t *pred_buf = NULL; + +void +ppred_init(int dom) +{ + init_table(ndesc, desctab, dom); +} + +int +ppred_getdesc(pmID pmid, pmDesc *desc) +{ + return getdesc(ndesc, desctab, pmid, desc); +} + +int +ppred_available(int item) +{ + extern int need_psusage; /* is psusage buffer needed or not */ + extern int need_accounting; /* is pracinfo buffer needed or not */ + + switch (item) { + case ITEM_SYSCALLS: + case ITEM_CTXSWITCH: + case ITEM_IODEMAND: + return need_psusage; + case ITEM_IOWAIT: + case ITEM_SCHEDWAIT: + return need_accounting; + case ITEM_VIRTUALSIZE: + case ITEM_RESIDENTSIZE: + /* need_psinfo - always have it */ + return 1; + } + return 1; +} + +int +ppred_setatom(int item, pmAtomValue *atom, int j) +{ + switch (item) { + case ITEM_SYSCALLS: + atom->f = pred_buf[j].syscalls; + break; + case ITEM_CTXSWITCH: + atom->f = pred_buf[j].ctxswitch; + break; + case ITEM_VIRTUALSIZE: + atom->f = pred_buf[j].virtualsize; + break; + case ITEM_RESIDENTSIZE: + atom->f = pred_buf[j].residentsize; + break; + case ITEM_IODEMAND: + atom->f = pred_buf[j].iodemand; + break; + case ITEM_IOWAIT: + atom->f = pred_buf[j].iowait; + break; + case ITEM_SCHEDWAIT: + atom->f = pred_buf[j].schedwait; + break; + } + return 0; +} + +int +ppred_getinfo(pid_t pid, int j) +{ + process_t *node; + char *path; + + node = lookup_curr_node(pid); + if (node == NULL) { + /* node should be there if it's in active list ! */ + (void)fprintf(stderr, "%s: Internal error for lookup_node()", pmProgname); + exit(1); + } + proc_pid_to_path(pid, NULL, &path, PINFO_PATH); + if (access(path, R_OK) < 0) + return -oserror(); + + pred_buf[j] = node->preds; + return 0; +} + + +int +ppred_allocbuf(int size) +{ + static int max_size = 0; + derived_pred_t *predb; + + if (size > max_size) { + predb = realloc(pred_buf, size * sizeof(derived_pred_t)); + if (predb == NULL) + return -oserror(); + pred_buf = predb; + max_size = size; + } + + return 0; +} diff --git a/src/pmdas/hotproc/src/ppred_values.h b/src/pmdas/hotproc/src/ppred_values.h new file mode 100644 index 0000000..22a2332 --- /dev/null +++ b/src/pmdas/hotproc/src/ppred_values.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PPRED_H +#define PPRED_H + +#define CLUSTER_PRED 101 + +#define ITEM_SYSCALLS 0 +#define ITEM_CTXSWITCH 1 +#define ITEM_VIRTUALSIZE 2 +#define ITEM_RESIDENTSIZE 3 +#define ITEM_IODEMAND 4 +#define ITEM_IOWAIT 5 +#define ITEM_SCHEDWAIT 6 + +void ppred_init(int dom); +int ppred_getdesc(pmID pmid, pmDesc *desc); +int ppred_setatom(int item, pmAtomValue *atom, int j); +int ppred_getinfo(pid_t pid, int j); +int ppred_allocbuf(int size); +int ppred_available(int item); + +#endif diff --git a/src/pmdas/infiniband/GNUmakefile b/src/pmdas/infiniband/GNUmakefile new file mode 100644 index 0000000..79991dc --- /dev/null +++ b/src/pmdas/infiniband/GNUmakefile @@ -0,0 +1,58 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2007-2009 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +CMDTARGET = pmdaib$(EXECSUFFIX) +CFILES = ib.c pmda.c +HFILES = ibpmda.h + +LSRCFILES = help root pmns Install Remove +LLDLIBS = $(IB_LIBS) $(PCP_LIBS) -lpcp_pmda -lpcp + +IAM = ib +DOMAIN = IB +PMDADIR = $(PCP_PMDAS_DIR)/infiniband +LDIRT = domain.h *.o $(IAM).log $(CMDTARGET) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(PMDA_INFINIBAND)" "" +build-me: domain.h $(CMDTARGET) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -S $(PMDADIR) $(PCP_PMDAS_DIR)/$(IAM) + $(INSTALL) -m 755 Install Remove $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 pmns root help domain.h $(PMDADIR) +else +build-me: +install: +endif + +ib.o: domain.h + +.NOTPARALLEL: +.ORDER: domain.h $(OBJECTS) + +default_pcp : default + +install_pcp : install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/infiniband/Install b/src/pmdas/infiniband/Install new file mode 100755 index 0000000..b674b88 --- /dev/null +++ b/src/pmdas/infiniband/Install @@ -0,0 +1,46 @@ +#! /bin/sh +# +# Copyright (C) 2013 Red Hat. +# Copyright (C) 2007,2008 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. /etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=infiniband +pmda_interface=3 +daemon_opt=true +dso_opt=false +pipe_opt=true +socket_opt=false + +path=/sys/class/infiniband_mad +if ! test -d $path; then + echo "Kernel lacks Infiniband support - $path directory not found" + exit 1 +fi + +__choose_mode() +{ + do_pmda=true +} + +__choose_ipc() +{ + ipc_type=pipe + type="pipe binary $PCP_PMDAS_DIR/infiniband/pmdaib" +} + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/infiniband/Remove b/src/pmdas/infiniband/Remove new file mode 100755 index 0000000..c35c41f --- /dev/null +++ b/src/pmdas/infiniband/Remove @@ -0,0 +1,24 @@ +#! /bin/sh +# +# Copyright (C) 2007,2008 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. /etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=infiniband + +pmdaSetup +pmdaRemove +exit 0 + diff --git a/src/pmdas/infiniband/help b/src/pmdas/infiniband/help new file mode 100644 index 0000000..93eaa7d --- /dev/null +++ b/src/pmdas/infiniband/help @@ -0,0 +1,190 @@ +# +# Copyright (c) 2007,2008 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +@ infiniband.hca.type Node type +Node type: channel adapter (CA), switch, router etc +@ infiniband.hca.ca_type HCA type +HCA type, e.g. MT23108, +@ infiniband.hca.numports Number of ports on HCA +Number of ports on HCA +@ infiniband.hca.fw_ver Version of HCA firmware +Version of HCA firmware +@ infiniband.hca.hw_ver Version of HCA hardware +Version of HCA hardware +@ infiniband.hca.node_guid Node's Global Unique Identifier +Node's Global Unique Identifier - 64 bit integer to refer to the node +@ infiniband.hca.system_guid System's Global Unique Identifier +System's Global Unique Identifier - 64 bit integer to refer to the system +@ infiniband.port.guid Port's Global Unique Identifier +Port's Global Unique Identifier - 64 bit integer to refer to the port +@ infiniband.port.gid_prefix GID prefix +GID prefix, assigned by subnet manager +@ infiniband.port.lid Port's Local Identifier +Port's Local Identifier, assigned by subnet manager +@ infiniband.port.state Port's state +Port's state - can be Active, Down, NoChange, Armed or Initialize +@ infiniband.port.phystate Port's physical state +Port's physical state +@ infiniband.port.rate Port's Data Rate +Port's Data Rate: 2, 5, 10 or 20 Gbps +@ infiniband.port.capabilities Port's capabilities +Port's capabilities. +@ infiniband.port.linkspeed Base link speed of the port. +This is a string which represents the base link speed of the port. +Multiplying link speed by link width gives port's data rate. +@ infiniband.port.linkwidth Port's link width. +Number of bi-directional Infiniband links active on the port. +Also known as X-factor, as in 1X, 4X, 12X. +@ infiniband.port.in.bytes Bytes received +Counter of data octets received on all VLs at the port. This +includes all octets between (and not including) the start of +packet delimiter and the VCRC, and may include packets containing errors. +It excludes all link packets. + +This counter is implemented by sampling underlying saturating PortRcvData +counter. When a value of saturated counter reaches predefined threshold, +the counter is reset after its value is copied into internal state. + +@ infiniband.port.in.packets Packets received +Counter of data packets received on all VLs at the port. This +may include packets containing errors but excludes all link packets. + +@ infiniband.port.in.errors.drop Packets dropped due to errors +Number of packets received on the port that were discarded because they +could not be forwarded by the switch relay due to DLID mapping, VL mapping +or looping. Implemented by sampling 16 bit PortRcvSwitchRelayErrors +counter. + +@ infiniband.port.in.errors.filter Packets filtered out +Number of packets received by the port that were discarded because +it was a raw packet and FilterRawInbound is enabled or because +PartitionEnforcementInbound is enabled and packet failed partition +key check or IP version check. Implemented by sampling 8 bit +PortRcvConstraintErrors counter. + +@ infiniband.port.in.errors.local Packets with errors +Counter of packets containing local physical errors, malformed data or +link packets or packets discarded due to buffer overrun. Implemented by +sampling 16 bit PortRcvErrors counter. + +@ infiniband.port.in.errors.remote Packets with EBP delimiter. +Number of packets marked with End Bad Packet delimited received by +the port. Implemented by sampling 16 bit PortRcvRemotePhysicalerrors +counter. + +@ infiniband.port.out.bytes Bytes transmitted +Counter of data octets, transmitted on all VLs from the port. This +includes all octets between (and not including) the start of +packet delimiter and the VCRC, and may include packets containing errors. +It excludes all link packets. + +This counter is implemented by sampling underlying saturating PortXmtData +counter. When a value of saturated counter reaches predefined threshold, +the counter is reset after its value is copied into internal state. + +@ infiniband.port.out.packets Packets transmitted +Counter of data packets transmitted on all VLs from the port. This +may include packets containing errors but excludes all link packets. + +@ infiniband.port.out.errors.drop Packets dropped without transmitting +Number of outbound packets which were droped because port is down +or congested. Implemented by sampling 16 bit PortXmtDiscard counter. + +@ infiniband.port.out.errors.filter Packets filtered out before transmitting +Number of packets not transmitted by the port because +it was a raw packet and FilterRawInbound is enabled or because +PartitionEnforcementInbound is enabled and packet failed partition +key check or IP version check. Implemented by sampling 8 bit +PortXmitConstraintErrors counter. + +@ infiniband.port.total.bytes Bytes transmitted and received +Cumulative value of infiniband.port.in.bytes and +infiniband.port.out.bytes, provided for convenience. + +@ infiniband.port.total.packets Packets transmitted and received +Cumulative value of infiniband.port.in.packets and +infiniband.port.out.packets, provided for convenience. + +@ infiniband.port.total.errors.drop Packet dropped +Cumulative counter of infiniband.port.in.errors.drop and +infiniband.out.errors.drops. + +@ infiniband.port.total.errors.filter Packet filtered out +Cumulative counter of infiniband.port.in.errors.filter and +infiniband.out.errors.filter. + +@ infiniband.port.total.errors.link Link downed +Number of times Port Training state machine has failed to +complete link recovery process and downed the link. Implemented by +sampling 8 bit LinkDownedCounter. + +@ infiniband.port.total.errors.recover Successful recoveries +Number of times Port Training state machine has managed successfully +complete link recovery process. Implemented by sampling 8 bit +LinkErrorRecoveryCounter. + +@ infiniband.port.total.errors.integrity Excessive local physical errors +Number of times the count of local physical errors exceeded the threshold. +Implemented by sampling 4 bit LocalLinkIntegrityErrors counter. + +@ infiniband.port.total.errors.vl15 Dropped packets to VL15 +Number of times packets to VL15 (management virtual line) was dropped +due to resource limitations. Implemented by sampling 16 bit VL15Dropped +counter. + +@ infiniband.port.total.errors.overrun Excessive Buffer Overruns +The number of times buffer overrun errors had persisted over multiple +flow control update times. Implemented by sampling 4 bit +ExcessiveBufferOverrun counter. + +@ infiniband.port.total.errors.symbol Total number of minor link errors +Total number of minor link errors detected on one or more physical lines. +Implemented by sampling 16 bit SymbolErrorCounter. + +@ infiniband.control.query_timeout Timeout for MAD perquery +Timeout in milliseconds for MAD rpcs. Default value is 1000 milliseconds. +Timeout can be set per port. + +@ infiniband.control.hiwat Counter threshold values +Threshold values for each MAD performance counter. Due to saturating +nature of the counters they're reset when value of a particular counter +gets above a threshold. Setting threshold to the maximum value disables +the reset mechanism. + +@ infiniband.port.switch.in.bytes Bytes received (using switch counter) +Counter for the bytes received by a port. This is calculated using the counter +of the switch the port is attached to. + +@ infiniband.port.switch.in.packets Packets received (using switch counter) +Counter for the packets received by a port. This is calculated using the +counter of the switch the port is attached to. + +@ infiniband.port.switch.out.bytes Bytes transmitted (using switch counter) +Counter for the bytes transmitted by a port. This is calculated using the +counter of the switch the port is attached to. + +@ infiniband.port.switch.out.packets Packets transmitted (using switch counter) +Counter for the packets transmitted by a port. This is calculated using the +counter of the switch the port is attached to. + +@ infiniband.port.switch.total.bytes Bytes transmitted and received (using switch counters) +Cumulative value of infiniband.port.switch.in.bytes and +infiniband.port.switch.out.bytes, provided for convenience. + +@ infiniband.port.switch.total.packets Packets transmitted and received (using switch counters) +Cumulative value of infiniband.port.switch.in.packets and +infiniband.port.switch.out.packets, provided for convenience. diff --git a/src/pmdas/infiniband/ib.c b/src/pmdas/infiniband/ib.c new file mode 100644 index 0000000..b3dc182 --- /dev/null +++ b/src/pmdas/infiniband/ib.c @@ -0,0 +1,1050 @@ +/* + * Copyright (C) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * IB part of the PMDA - initialization, fetching etc. + */ +#include "ibpmda.h" +#include +#include +#include + +#define IBPMDA_MAX_HCAS (16) + +typedef struct local_port_s { + /* + * Cache the ca_name and portnum to avoid a bug in libibumad that + * leaks memory when umad_port_get() is called over and over. + * With ca_name and portnum we can safely do umad_port_release() + * first and then umad_port_get() without fear that some future + * version of release() will deallocate port->ca_name and + * port->portnum. + */ + char ca_name[UMAD_CA_NAME_LEN]; + int portnum; + umad_port_t *ump; + void * hndl; + int needsupdate; +} local_port_t; + +/* umad_ca_t starts with a name which is good enough for us to use */ +typedef struct hca_state_s { + umad_ca_t ca; + local_port_t lports[UMAD_CA_MAX_PORTS]; +} hca_state_t; + +/* IB Architecture rel 1.2 demands that performance counters + * must plateau once they reach 2^32. This structure is used + * to track the counters and reset them when they get close to + * the magic boundary */ +typedef struct mad_counter_s { + uint64_t accum; /* Accumulated value */ + uint32_t prev; /* Previous value of the counter */ + uint32_t cur; /* Current value, only valid during iteration */ + uint32_t isvalid; /* Is current value valid? */ +} mad_counter_t; + +typedef struct mad_cnt_desc_s { + enum MAD_FIELDS madid; /* ID for the counter */ + char *name; + int resetmask; /* Reset mask for port_performance_reset */ + uint32_t hiwat; /* If current value is over hiwat mark, reset it */ + int multiplier; +} mad_cnt_desc_t; + +#define MADDESC_INIT(id, mask, shft, mul) \ + [ IBPMDA_##id ] {IB_PC_##id##_F, #id, (1<= UMAD_CA_MAX_PORTS) || (lport < 0)) { + print_parse_err(LOG_ERR, + "port number %d is out of bounds for HCA %s\n", + lport, local); + return; + } + + if (hca->lports[lport].hndl == NULL) { + print_parse_err(LOG_ERR, + "port %s:%d has failed initialization\n", + local, lport); + return; + } + + if ((ps = (port_state_t *)calloc(1, sizeof(port_state_t))) == NULL) { + __pmNotifyErr (LOG_ERR, "Out of memory to save state for %s\n", name); + return; + } + + ps->guid = guid; + ps->remport = rport; + ps->lport = hca->lports + lport; + ps->portid.lid = -1; + ps->timeout = 1000; + + if ((inst = pmdaCacheStore(itab[IB_PORT_INDOM].it_indom, + PMDA_CACHE_ADD, name, ps)) < 0) { + __pmNotifyErr(LOG_ERR, "Cannot add %s to the cache - %s\n", + name, pmErrStr(inst)); + free (ps); + return; + } + + portcount++; +} + + +static int +foreachport(hca_state_t *hst, void (*cb)(hca_state_t *, umad_port_t *, void *), + void *closure) +{ + int pcnt = hst->ca.numports; + int p; + int nports = 0; + + for (p=0; (pcnt >0) && (p < UMAD_CA_MAX_PORTS); p++) { + umad_port_t *port = hst->ca.ports[p]; + + if (port ) { + pcnt--; + nports++; + if (cb) { + cb (hst, port, closure); + } + } + } + return (nports); +} + +#ifdef HAVE_NETWORK_BYTEORDER +#define guid_htonll(a) do { } while (0) /* noop */ +#define guid_ntohll(a) do { } while (0) /* noop */ +#else +static void +guid_htonll(char *p) +{ + char c; + int i; + + for (i = 0; i < 4; i++) { + c = p[i]; + p[i] = p[7-i]; + p[7-i] = c; + } +} +#define guid_ntohll(v) guid_htonll(v) +#endif + +static void +printportconfig (hca_state_t *hst, umad_port_t *port, void *arg) +{ + uint64_t hguid = port->port_guid; + + guid_ntohll((char *)&hguid); + + fprintf (fconf, "%s:%d 0x%llx %d via %s:%d\n", + port->ca_name, port->portnum, (unsigned long long)hguid, + port->portnum, hst->ca.ca_name, port->portnum); +} + +static void +monitorport(hca_state_t *hst, umad_port_t *port, void *arg) +{ + pmdaIndom *itab = arg; + uint64_t hguid = port->port_guid; + char name[128]; + + guid_ntohll((char *)&hguid); + sprintf(name, "%s:%d", port->ca_name, port->portnum); + + monitor_guid(itab, name, hguid, port->portnum, port->ca_name, port->portnum); +} + + +static int mgmt_classes[] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, + IB_SA_CLASS, IB_PERFORMANCE_CLASS}; +static void +openumadport (hca_state_t *hst, umad_port_t *port, void *arg) +{ + void *hndl = arg; + local_port_t *lp; + + if ((hndl = mad_rpc_open_port(port->ca_name, port->portnum, mgmt_classes, + ARRAYSZ(mgmt_classes))) == NULL) { + __pmNotifyErr(LOG_ERR, "Cannot open port handle for %s:%d\n", + port->ca_name, port->portnum); + } + lp = &hst->lports[port->portnum]; + strcpy(lp->ca_name, port->ca_name); + lp->portnum = port->portnum; + lp->ump = port; + lp->hndl = hndl; +} + +static void +parse_config(pmdaIndom *itab) +{ + char buffer[2048]; + + while ((fgets(buffer, sizeof(buffer)-1, fconf)) != NULL) { + char *p; + + lcnt++; + + /* strip comments */ + if ((p = strchr(buffer,'#'))) + *p='\0'; + + for (p = buffer; *p; p++) { + if (!isspace (*p)) + break; + } + + if (*p != '\0') { + char name[128]; + long long guid; + int rport; + char local[128]; + int lport; + + if (sscanf(p, "%[^ \t]%llx%d via %[^:]:%d", + name, &guid, &rport, local, &lport) != 5) { + __pmNotifyErr (LOG_ERR, "%s(%d): cannot parse the line\n", + confpath, lcnt); + continue; + } + + monitor_guid(itab, name, guid, rport, local, lport); + } + } +} + +int +ib_load_config(const char *cp, int writeconf, pmdaIndom *itab, unsigned int nindoms) +{ + char hcas[IBPMDA_MAX_HCAS][UMAD_CA_NAME_LEN]; + hca_state_t *st = NULL; + int i, n; + int (*closef)(FILE *) = fclose; + + if (nindoms <= IB_CNT_INDOM) + return -EINVAL; + + if (umad_init()) { + __pmNotifyErr(LOG_ERR, + "umad_init() failed. No IB kernel support or incorrect ABI version\n"); + return -EIO; + } + + if ((n = umad_get_cas_names(hcas, ARRAYSZ(hcas)))) { + if ((st = calloc (n, sizeof(hca_state_t))) == NULL) + return -ENOMEM; + } else + /* No HCAs */ + return 0; + + /* Open config file - if the executable bit is set then assume that + * user wants it to be a script and run it, otherwise try loading it. + */ + strcpy(confpath, cp); + if (access(confpath, F_OK) == 0) { + if (writeconf) { + __pmNotifyErr(LOG_ERR, + "Config file exists and writeconf arg was given to pmdaib. Aborting."); + exit(1); + } + + if (access(confpath, X_OK)) { + /* Not an executable, just read it */ + fconf = fopen (confpath, "r"); + } else { + fconf = popen(confpath, "r"); + closef = pclose; + } + } else if (writeconf) { + fconf = fopen(confpath, "w"); + } + /* else no config file: Just monitor local ports */ + + for (i=0; i < n; i++) { + if (umad_get_ca(hcas[i], &st[i].ca) == 0) { + int e = pmdaCacheStore(itab[IB_HCA_INDOM].it_indom, PMDA_CACHE_ADD, + st[i].ca.ca_name, &st[i].ca); + + if (e < 0) { + __pmNotifyErr(LOG_ERR, + "Cannot add instance for %s to the cache - %s\n", + st[i].ca.ca_name, pmErrStr(e)); + continue; + } + + foreachport(st+i, openumadport, NULL); + if (fconf == NULL) + /* No config file - monitor local ports */ + foreachport(st+i, monitorport, itab); + if (writeconf) + foreachport(st+i, printportconfig, fconf); + } + } + + if (fconf) { + parse_config(itab); + (*closef)(fconf); + } + + if (writeconf) + /* Config file is now written. Exit. */ + exit(0); + + if (!portcount) { + __pmNotifyErr(LOG_INFO, "No IB ports found to monitor"); + } + + itab[IB_CNT_INDOM].it_set = (pmdaInstid *)calloc(ARRAYSZ(mad_cnt_descriptors), + sizeof(pmdaInstid)); + + if (itab[IB_CNT_INDOM].it_set == NULL) { + return -ENOMEM; + } + + itab[IB_CNT_INDOM].it_numinst = ARRAYSZ(mad_cnt_descriptors); + for (i=0; i < ARRAYSZ(mad_cnt_descriptors); i++) { + itab[IB_CNT_INDOM].it_set[i].i_inst = i; + itab[IB_CNT_INDOM].it_set[i].i_name = mad_cnt_descriptors[i].name; + + } + + return 0; +} + +static char * +ib_portcap_to_string(port_state_t *pst) +{ + static struct { + int bit; + const char *cap; + } capdest [] = { + {1, "SM"}, + {2, "Notice"}, + {3, "Trap"}, + {5, "AutomaticMigration"}, + {6, "SLMapping"}, + {7, "MKeyNVRAM"}, + {8, "PKeyNVRAM"}, + {9, "LedInfo"}, + {10, "SMdisabled"}, + {11, "SystemImageGUID"}, + {12, "PkeySwitchExternalPortTrap"}, + {16, "CommunicatonManagement"}, + {17, "SNMPTunneling"}, + {18, "Reinit"}, + {19, "DeviceManagement"}, + {20, "VendorClass"}, + {21, "DRNotice"}, + {22, "CapabilityMaskNotice"}, + {23, "BootManagement"}, + {24, "IsLinkRoundTripLatency"}, + {25, "ClientRegistration"} + }; + char *comma = ""; + int commalen = 0; + int i; + char *ptr = pst->pcap; + uint32_t bsiz = sizeof(pst->pcap); + int pcap = mad_get_field(pst->portinfo, 0, IB_PORT_CAPMASK_F); + + *ptr ='\0'; + + for (i=0; i < ARRAYSZ(capdest); i++) { + if (pcap & (1<pcap); +} + + +/* This function can be called multiple times during single + * fetch operation so take care to avoid side effects, for example, + * if the "previous" value of the counter is above the high + * watermark and must be reset, don't change the previous value here - + * it could lead to double counting on the second call */ +static uint64_t +ib_update_perfcnt (port_state_t *pst, int udata, int *rv ) +{ + mad_cnt_desc_t * md = mad_cnt_descriptors + udata; + mad_counter_t *mcnt = pst->madcnts + udata; + + if (!mcnt->isvalid) { + uint32_t delta; + + mcnt->cur = mad_get_field(pst->perfdata, 0, md->madid); + mcnt->isvalid = 1; + + /* If someone resets the counters, then don't update the the + * accumulated value because we don't know what was the value before it + * was reset. And if the difference between current and previous value + * is larger then the high watermark then don't update the accumulated + * value either - current value could've pegged because we didn't + * fetch often enough */ + delta = mcnt->cur - mcnt->prev; + if ((mcnt->cur < mcnt->prev) || (delta > md->hiwat)) { + mcnt->isvalid = PM_ERR_VALUE; + } else { + mcnt->accum += delta; + } + + if (mcnt->cur > md->hiwat) { + pst->resetmask |= md->resetmask; + } + } + + *rv = mcnt->isvalid; + return (mcnt->accum * md->multiplier); +} + +static int +ib_linkwidth (port_state_t *pst) +{ + int w = mad_get_field(pst->portinfo, 0, IB_PORT_LINK_WIDTH_ACTIVE_F); + + switch (w) { + case 1: + return (1); + case 2: + return (4); + case 4: + return (8); + case 8: + return (12); + } + return (0); +} + +int +ib_fetch_val(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmInDom_int *ind = (__pmInDom_int *)&(mdesc->m_desc.indom); + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int rv = 1; + port_state_t *pst = NULL; + hca_state_t *hca = NULL; + int umask = 1<cluster; + int udata = (int)((__psint_t)mdesc->m_user); + void *closure = NULL; + int st; + char *name = NULL; + + if (inst == PM_INDOM_NULL) { + return PM_ERR_INST; + } + + if (ind->serial != IB_CNT_INDOM) { + if ((st = pmdaCacheLookup (mdesc->m_desc.indom, inst, &name, + &closure)) != PMDA_CACHE_ACTIVE) { + if (st == PMDA_CACHE_INACTIVE) + st = PM_ERR_INST; + __pmNotifyErr (LOG_ERR, "Cannot find instance %d in indom %s: %s\n", + inst, pmInDomStr(mdesc->m_desc.indom), pmErrStr(st)); + return st; + } + } + + /* If fetching from HCA indom, then no refreshing is necessary for the + * lifetime of a pmda. Ports could change state, so some update could be + * necessary */ + switch (ind->serial) { + case IB_PORT_INDOM: + if (idp->cluster > 3) { + return PM_ERR_INST; + } + + pst = closure; + + if (pst->needupdate & umask) { + local_port_t *lp = pst->lport; + + /* A port state is considered up-to date regardless of any + * errors which could happen later - this is used to implement + * one shot updates */ + pst->needupdate ^= umask; + + /* The state of the local port used for queries is checked + * once per fetch request */ + if (lp->needsupdate) { + umad_release_port(lp->ump); + if (umad_get_port(lp->ca_name, lp->portnum, lp->ump) != 0) { + __pmNotifyErr (LOG_ERR, + "Cannot get state of the port %s:%d\n", + lp->ump->ca_name, lp->ump->portnum); + return 0; + } + lp->needsupdate = 0; + } + + /* If the port which we're supposed to use to query the data + * does not have a LID then we don't even try to query anything, + * is it going to fail anyway */ + if (lp->ump->base_lid == 0) { + return 0; /* No values available */ + } + + if (pst->portid.lid < 0) { + ib_portid_t sm = {0}; + sm.lid = lp->ump->sm_lid; + + memset (&pst->portid, 0, sizeof (pst->portid)); + if (ib_resolve_guid_via (&pst->portid, &pst->guid, &sm, + pst->timeout, lp->hndl) < 0) { + __pmNotifyErr (LOG_ERR, + "Cannot resolve GUID 0x%llx for %s " + "via %s:%d\n", + (unsigned long long)pst->guid, + name, lp->ump->ca_name, + lp->ump->portnum); + pst->portid.lid = -1; + return 0; + } + } + + switch (idp->cluster) { + case 0: /* port attributes */ + memset (pst->portinfo, 0, sizeof(pst->portinfo)); + if (!smp_query_via (pst->portinfo, &pst->portid, + IB_ATTR_PORT_INFO, 0, pst->timeout, + lp->hndl)) { + __pmNotifyErr (LOG_ERR, + "Cannot get port info for %s via %s:%d\n", + name, lp->ump->ca_name, lp->ump->portnum); + return 0; + } + break; + + case 1: /* performance counters */ + /* I thought about updating all accumulating counters + * in case port_performance_query() succeeds but + * decided not to do it right now - updating all counters + * could mean more resets even in case when nobody is + * actually looking at the particular counter and I'm + * trying to minimize resets. */ + memset (pst->perfdata, 0, sizeof (pst->perfdata)); + if (!port_perf_query(pst->perfdata, &pst->portid, + pst->remport, pst->timeout, lp->hndl)) { + __pmNotifyErr (LOG_ERR, + "Cannot get performance counters for %s " + "via %s:%d\n", + name, lp->ump->ca_name, lp->ump->portnum); + return 0; + } + break; + + case 3: { /* switch performance counters */ + +#ifdef HAVE_PMA_QUERY_VIA + + // To find the LID of the switch the HCA is connected to, + // send an SMP on the directed route 0,1 and ask the port + // to identify itself. + ib_portid_t sw_port_id = { + .drpath = { + .cnt = 1, + .p = { 0, 1, }, + }, + }; + + uint8_t sw_info[64]; + memset(sw_info, 0, sizeof(sw_info)); + if (!smp_query_via(sw_info, &sw_port_id, IB_ATTR_PORT_INFO, 0, + pst->timeout, lp->hndl)) { + __pmNotifyErr(LOG_ERR, + "Cannot get switch port info for %s via %s:%d.\n", + name, lp->ump->ca_name, lp->ump->portnum); + return 0; + } + + int sw_lid, sw_port; + mad_decode_field(sw_info, IB_PORT_LID_F, &sw_lid); + mad_decode_field(sw_info, IB_PORT_LOCAL_PORT_F, &sw_port); + + sw_port_id.lid = sw_lid; + + // Query for the switch's performance counters' values. + memset(pst->switchperfdata, 0, sizeof(pst->switchperfdata)); + if (!pma_query_via(pst->switchperfdata, &sw_port_id, sw_port, + pst->timeout, IB_GSI_PORT_COUNTERS_EXT, lp->hndl)) { + __pmNotifyErr(LOG_ERR, + "Cannot query performance counters of switch LID %d, port %d.\n", + sw_lid, sw_port); + return 0; + } +#endif + break; + } + + } + pst->validstate ^= umask; + } else if (!(pst->validstate & umask)) { + /* We've hit an error on the previous update - continue + * reporting no data for this instance */ + return (0); + } + break; + + case IB_HCA_INDOM: + hca = closure; + break; + + case IB_CNT_INDOM: + break; + + default: + return (PM_ERR_INST); + } + + switch (idp->cluster) { + case 0: /* UMAD data - hca name, fw_version, number of ports etc */ + switch(idp->item) { + case METRIC_ib_hca_hw_ver: + atom->cp = hca->ca.hw_ver; + break; + + case METRIC_ib_hca_system_guid: + atom->ull = hca->ca.system_guid; + break; + + case METRIC_ib_hca_node_guid: + atom->ull = hca->ca.node_guid; + break; + + case METRIC_ib_hca_numports: + atom->l = hca->ca.numports; + break; + + case METRIC_ib_hca_type: + if (hca->ca.node_type < ARRAYSZ(node_types)) { + atom->cp = node_types[hca->ca.node_type]; + } else { + __pmNotifyErr (LOG_INFO, "Unknown node type %d for %s\n", + hca->ca.node_type, hca->ca.ca_name); + atom->cp = "Unknown"; + } + break; + + case METRIC_ib_hca_fw_ver: + atom->cp = hca->ca.fw_ver; + break; + + case METRIC_ib_port_gid_prefix: + atom->ull = mad_get_field64(pst->portinfo, 0, IB_PORT_GID_PREFIX_F); + break; + + case METRIC_ib_port_rate: + atom->l = ib_linkwidth(pst) * + (5 * mad_get_field (pst->portinfo, 0, + IB_PORT_LINK_SPEED_ACTIVE_F))/2; + break; + + case METRIC_ib_port_lid: + atom->l = pst->portid.lid; + break; + + case METRIC_ib_port_capabilities: + atom->cp = ib_portcap_to_string(pst); + break; + + case METRIC_ib_port_phystate: + st = mad_get_field (pst->portinfo, 0, IB_PORT_PHYS_STATE_F); + if (st < ARRAYSZ(port_phystates)) { + atom->cp = port_phystates[st]; + } else { + __pmNotifyErr (LOG_INFO, "Unknown port PHY state %d on %s\n", + st, name); + atom->cp = "Unknown"; + } + break; + + case METRIC_ib_port_guid: + atom->ull = pst->guid; + break; + + case METRIC_ib_hca_ca_type: + atom->cp = hca->ca.ca_type; + break; + + case METRIC_ib_port_state: + st = mad_get_field (pst->portinfo, 0, IB_PORT_STATE_F); + if (st < ARRAYSZ(port_states)) { + atom->cp = port_states[st]; + } else { + __pmNotifyErr (LOG_INFO, "Unknown port state %d on %s\n", + st, name); + atom->cp = "Unknown"; + } + break; + + case METRIC_ib_port_linkspeed: + switch ((st = mad_get_field(pst->portinfo, 0, + IB_PORT_LINK_SPEED_ACTIVE_F))) { + case 1: + atom->cp = "2.5 Gpbs"; + break; + case 2: + atom->cp = "5.0 Gbps"; + break; + case 4: + atom->cp = "10.0 Gbps"; + break; + default: + __pmNotifyErr (LOG_INFO, "Unknown link speed %d on %s\n", + st, name); + atom->cp = "Unknown"; + break; + } + break; + + case METRIC_ib_port_linkwidth: + atom->l = ib_linkwidth(pst); + break; + + default: + rv = PM_ERR_PMID; + break; + } + break; + + case 1: /* Fetch values from mad rpc response */ + if ((udata >= 0) && (udata < ARRAYSZ(mad_cnt_descriptors))) { + /* If a metric has udata set then it's one of the "direct" + * metrics - just update the accumulated counter + * and stuff its value into pmAtomValue */ + switch (mdesc->m_desc.type) { + case PM_TYPE_32: + atom->l = (int32_t)ib_update_perfcnt (pst, udata, &rv); + break; + case PM_TYPE_64: + atom->ll = ib_update_perfcnt (pst, udata, &rv); + break; + default: + rv = PM_ERR_INST; + break; + } + } else { + int rv1=0, rv2=0; + /* Synthetic metrics */ + switch (idp->item) { + case METRIC_ib_port_total_bytes: + atom->ll = ib_update_perfcnt (pst, IBPMDA_XMT_BYTES, &rv1) + + ib_update_perfcnt (pst, IBPMDA_RCV_BYTES, &rv2); + break; + + case METRIC_ib_port_total_packets: + atom->ll = ib_update_perfcnt (pst, IBPMDA_XMT_PKTS, &rv1) + + ib_update_perfcnt (pst, IBPMDA_RCV_PKTS, &rv2); + break; + + case METRIC_ib_port_total_errors_drop: + atom->l = (int)(ib_update_perfcnt (pst, IBPMDA_ERR_SWITCH_REL, &rv1) + + ib_update_perfcnt (pst, IBPMDA_XMT_DISCARDS, &rv2)); + break; + + case METRIC_ib_port_total_errors_filter: + atom->l = (int)(ib_update_perfcnt (pst, IBPMDA_ERR_XMTCONSTR, &rv1) + + ib_update_perfcnt (pst, IBPMDA_ERR_RCVCONSTR, &rv2)); + break; + + default: + rv = PM_ERR_PMID; + break; + } + + if ((rv1 < 0) || (rv2 < 0)) { + rv = (rv1 < 0) ? rv1 : rv2; + } + } + break; + + case 2: /* Control structures */ + switch (idp->item) { + case METRIC_ib_control_query_timeout: + atom->l = pst->timeout; + break; + + case METRIC_ib_control_hiwat: + if (inst < ARRAYSZ(mad_cnt_descriptors)) { + atom->ul = mad_cnt_descriptors[inst].hiwat; + } else { + rv = PM_ERR_INST; + } + break; + + default: + rv = PM_ERR_PMID; + break; + } + break; + + case 3: /* Fetch values from switch response */ + +#ifdef HAVE_PMA_QUERY_VIA + + // (The values are "swapped" because what the port receives is what the + // switch sends, and vice versa.) + switch (idp->item) { + case METRIC_ib_port_switch_in_bytes: { + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_XMT_BYTES_F, &atom->ull); + atom->ull *= 4; // TODO: programmatically determine link width + break; + } + case METRIC_ib_port_switch_in_packets: { + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_XMT_PKTS_F, &atom->ull); + break; + } + case METRIC_ib_port_switch_out_bytes: { + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_RCV_BYTES_F, &atom->ull); + atom->ull *= 4; // TODO: programmatically determine link width + break; + } + case METRIC_ib_port_switch_out_packets: { + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_RCV_PKTS_F, &atom->ull); + break; + } + case METRIC_ib_port_switch_total_bytes: { + uint64_t sw_rx_bytes, sw_tx_bytes; + int ib_lw; + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_RCV_BYTES_F, &sw_rx_bytes); + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_XMT_BYTES_F, &sw_tx_bytes); + ib_lw = 4; // TODO: programmatically determine link width + atom->ull = (sw_rx_bytes * ib_lw) + (sw_tx_bytes * ib_lw); + break; + } + case METRIC_ib_port_switch_total_packets: { + uint64_t sw_rx_packets, sw_tx_packets; + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_RCV_PKTS_F, &sw_rx_packets); + mad_decode_field(pst->switchperfdata, + IB_PC_EXT_XMT_PKTS_F, &sw_tx_packets); + atom->ull = sw_rx_packets + sw_tx_packets; + break; + } + default: { + rv = PM_ERR_PMID; + break; + } + } +#else + + return PM_ERR_VALUE; + +#endif + break; + + default: + rv = PM_ERR_PMID; + break; + } + + return rv; +} + +/* Walk the instances and arm needupdate flag in each instance's + * state. The actuall updating is done in the fetch function */ +void +ib_rearm_for_update(void *state) +{ + port_state_t *pst = state; + + pst->lport->needsupdate = 1; + + pst->needupdate = IB_PORTINFO_UPDATE | IB_HCA_PERF_UPDATE | IB_SWITCH_PERF_UPDATE; + pst->validstate = 4; /* 0x4 for timeout which is always valid */ +} + +void +ib_reset_perfcounters (void *state) +{ + int m; + port_state_t *pst = state; + + if (pst->resetmask && (pst->portid.lid != 0)) { + memset (pst->perfdata, 0, sizeof (pst->perfdata)); + + if (port_perf_reset(pst->perfdata, &pst->portid, pst->remport, + pst->resetmask, pst->timeout, pst->lport->hndl)) { + int j; + + for (j=0; j < ARRAYSZ(mad_cnt_descriptors); j++) { + if (pst->resetmask & (1<madcnts[j].prev = 0; + pst->madcnts[j].isvalid = 0; + } + } + } + } + pst->resetmask = 0; + + for (m=0; m < ARRAYSZ(mad_cnt_descriptors); m++) { + if (pst->madcnts[m].isvalid) { + pst->madcnts[m].prev = pst->madcnts[m].cur; + pst->madcnts[m].isvalid = 0; + } + } +} + +int +ib_store(pmResult *result, pmdaExt *pmda) +{ + int i; + + for (i = 0; i < result->numpmid ; i++) { + pmValueSet *vs = result->vset[i]; + __pmID_int *pmidp = (__pmID_int *)&vs->pmid; + int inst; + + if (pmidp->cluster != 2) { + return (-EACCES); + } + + if (vs->valfmt != PM_VAL_INSITU) { + return (-EINVAL); + } + + for (inst=0; inst < vs->numval; inst++) { + int id = vs->vlist[inst].inst; + void *closure = NULL; + + switch (pmidp->item) { + case METRIC_ib_control_query_timeout: + if (pmdaCacheLookup (pmda->e_indoms[IB_PORT_INDOM].it_indom, + id, NULL, &closure) == PMDA_CACHE_ACTIVE) { + port_state_t *pst = closure; + pst->timeout = vs->vlist[inst].value.lval; + } else { + return (PM_ERR_INST); + } + break; + + case METRIC_ib_control_hiwat: + if ((id < 0) || + (id > pmda->e_indoms[IB_CNT_INDOM].it_numinst)) { + return (PM_ERR_INST); + } + + mad_cnt_descriptors[id].hiwat = (uint32_t)vs->vlist[inst].value.lval; + break; + + default: + return (-EACCES); + } + } + } + return 0; +} + diff --git a/src/pmdas/infiniband/ibpmda.h b/src/pmdas/infiniband/ibpmda.h new file mode 100644 index 0000000..a9bbafa --- /dev/null +++ b/src/pmdas/infiniband/ibpmda.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 Red Hat. + * Copyright (C) 2007,2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _IBPMDA_H +#define _IBPMDA_H + +#include +#include +#include +#include "domain.h" + +#ifdef HAVE_PORT_PERFORMANCE_QUERY_VIA +#define port_perf_query(data, dst, port, timeout, srcport) \ + port_performance_query_via(data, dst, port, timeout, srcport) +#define port_perf_reset(data, dst, port, mask, timeout, srcport) \ + port_performance_reset_via(data, dst, port, mask, timeout, srcport) +#else +#define port_perf_query(data, dst, port, timeout, srcport) \ + pma_query_via(data, dst, port, timeout, IB_GSI_PORT_COUNTERS, srcport) +#define port_perf_reset(data, dst, port, mask, timeout, srcport) \ + performance_reset_via(data, dst, port, mask, timeout, IB_GSI_PORT_COUNTERS, srcport) +#endif + + +void ibpmda_init (const char *configpath, int, pmdaInterface *); + +int ib_fetch_val(pmdaMetric *, unsigned int, pmAtomValue *); +int ib_load_config(const char *, int, pmdaIndom *, unsigned int); +void ib_rearm_for_update(void *); +void ib_reset_perfcounters (void *); +int ib_store(pmResult *, pmdaExt *); + +#define IB_HCA_INDOM 0 +#define IB_PORT_INDOM 1 +#define IB_CNT_INDOM 2 + +#define IB_PORTINFO_UPDATE 0x1 +#define IB_HCA_PERF_UPDATE 0x2 +#define IB_SWITCH_PERF_UPDATE 0x8 + +#define ARRAYSZ(a) (sizeof(a)/sizeof(a[0])) + +#define METRIC_ib_hca_type 0 +#define METRIC_ib_hca_ca_type 1 +#define METRIC_ib_hca_numports 2 +#define METRIC_ib_hca_fw_ver 3 +#define METRIC_ib_hca_hw_ver 4 +#define METRIC_ib_hca_node_guid 5 +#define METRIC_ib_hca_system_guid 6 +#define METRIC_ib_port_guid 7 +#define METRIC_ib_port_gid_prefix 8 +#define METRIC_ib_port_lid 9 +#define METRIC_ib_port_state 10 +#define METRIC_ib_port_phystate 11 +#define METRIC_ib_port_rate 12 +#define METRIC_ib_port_capabilities 13 +#define METRIC_ib_port_linkspeed 14 +#define METRIC_ib_port_linkwidth 15 + +/* Per-port performance counters, cluster #1 */ +#define METRIC_ib_port_in_bytes 0 +#define METRIC_ib_port_in_packets 1 +#define METRIC_ib_port_out_bytes 2 +#define METRIC_ib_port_out_packets 3 +#define METRIC_ib_port_in_errors_drop 4 +#define METRIC_ib_port_out_errors_drop 5 +#define METRIC_ib_port_total_bytes 6 +#define METRIC_ib_port_total_packets 7 +#define METRIC_ib_port_total_errors_drop 8 +#define METRIC_ib_port_in_errors_filter 9 +#define METRIC_ib_port_in_errors_local 10 +#define METRIC_ib_port_in_errors_remote 11 +#define METRIC_ib_port_out_errors_filter 12 +#define METRIC_ib_port_total_errors_filter 13 +#define METRIC_ib_port_total_errors_link 14 +#define METRIC_ib_port_total_errors_recover 15 +#define METRIC_ib_port_total_errors_integrity 16 +#define METRIC_ib_port_total_errors_vl15 17 +#define METRIC_ib_port_total_errors_overrun 18 +#define METRIC_ib_port_total_errors_symbol 19 + +/* Control metrics */ +#define METRIC_ib_control_query_timeout 0 +#define METRIC_ib_control_hiwat 1 + +/* Per-port switch performance counters, cluster #3 */ +#define METRIC_ib_port_switch_in_bytes 0 +#define METRIC_ib_port_switch_in_packets 1 +#define METRIC_ib_port_switch_out_bytes 2 +#define METRIC_ib_port_switch_out_packets 3 +#define METRIC_ib_port_switch_total_bytes 4 +#define METRIC_ib_port_switch_total_packets 5 + +enum ibpmd_cndid { + IBPMDA_ERR_SYM = 0, + IBPMDA_LINK_RECOVERS, + IBPMDA_LINK_DOWNED, + IBPMDA_ERR_RCV, + IBPMDA_ERR_PHYSRCV, + IBPMDA_ERR_SWITCH_REL, + IBPMDA_XMT_DISCARDS, + IBPMDA_ERR_XMTCONSTR, + IBPMDA_ERR_RCVCONSTR, + IBPMDA_ERR_LOCALINTEG, + IBPMDA_ERR_EXCESS_OVR, + IBPMDA_VL15_DROPPED, + IBPMDA_XMT_BYTES, + IBPMDA_RCV_BYTES, + IBPMDA_XMT_PKTS, + IBPMDA_RCV_PKTS +}; + +#endif /* _IBPMDA_H */ diff --git a/src/pmdas/infiniband/pmda.c b/src/pmdas/infiniband/pmda.c new file mode 100644 index 0000000..20c5ba1 --- /dev/null +++ b/src/pmdas/infiniband/pmda.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2013 Red Hat. + * Copyright (C) 2007,2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ibpmda.h" + +/* + * Metric Table + */ +pmdaMetric metrictab[] = { + /* infiniband.hca.type */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_type), + PM_TYPE_STRING, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.hca.ca_type */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_ca_type), + PM_TYPE_STRING, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.hca.numports */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_numports), + PM_TYPE_32, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.hca.fw_ver */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_fw_ver), + PM_TYPE_STRING, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.hca.hw_ver */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_hw_ver), + PM_TYPE_STRING, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.hca.node_guid */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_node_guid), + PM_TYPE_U64, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.hca.system_guid */ + { NULL, + {PMDA_PMID(0,METRIC_ib_hca_system_guid), + PM_TYPE_U64, IB_HCA_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.guid */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_guid), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.gid_prefix */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_gid_prefix), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.lid */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_lid), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.state */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_state), + PM_TYPE_STRING, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.phystate */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_phystate), + PM_TYPE_STRING, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.rate */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_rate), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.capabilities */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_capabilities), + PM_TYPE_STRING, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.linkspeed */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_linkspeed), + PM_TYPE_STRING, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.linkwidth */ + { NULL, + {PMDA_PMID(0,METRIC_ib_port_linkwidth), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* infiniband.port.in.bytes */ + { (void *)IBPMDA_RCV_BYTES, + {PMDA_PMID(1,METRIC_ib_port_in_bytes), + PM_TYPE_64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + + /* infiniband.port.in.packets */ + { (void *)IBPMDA_RCV_PKTS, + {PMDA_PMID(1,METRIC_ib_port_in_packets), + PM_TYPE_64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) }, }, + + /* infiniband.port.in.errors.drop */ + { (void *)IBPMDA_ERR_SWITCH_REL, + {PMDA_PMID(1,METRIC_ib_port_in_errors_drop), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.in.errors.filter */ + { (void *)IBPMDA_ERR_RCVCONSTR, + {PMDA_PMID(1,METRIC_ib_port_in_errors_filter), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.in.errors.local */ + { (void *)IBPMDA_ERR_RCV, + {PMDA_PMID(1,METRIC_ib_port_in_errors_local), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.in.errors.filter */ + { (void *)IBPMDA_ERR_PHYSRCV, + {PMDA_PMID(1,METRIC_ib_port_in_errors_remote), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.out.bytes */ + { (void *)IBPMDA_XMT_BYTES, + {PMDA_PMID(1,METRIC_ib_port_out_bytes), + PM_TYPE_64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + + /* infiniband.port.out.packets */ + {(void *)IBPMDA_XMT_PKTS, + {PMDA_PMID(1,METRIC_ib_port_out_packets), + PM_TYPE_64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) }, }, + + /* infiniband.port.out.errors.drop */ + { (void *)IBPMDA_XMT_DISCARDS, + {PMDA_PMID(1,METRIC_ib_port_out_errors_drop), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.out.errors.filter */ + { (void *)IBPMDA_ERR_XMTCONSTR, + {PMDA_PMID(1,METRIC_ib_port_out_errors_filter), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.bytes */ + { (void *)-1, + {PMDA_PMID(1,METRIC_ib_port_total_bytes), + PM_TYPE_64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + /* infiniband.port.total.packets */ + { (void *)-1, + {PMDA_PMID(1,METRIC_ib_port_total_packets), + PM_TYPE_64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.drop */ + { (void *)-1, + {PMDA_PMID(1,METRIC_ib_port_total_errors_drop), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.filter */ + { (void *)-1, + {PMDA_PMID(1,METRIC_ib_port_total_errors_filter), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.link */ + { (void *)IBPMDA_LINK_DOWNED, + {PMDA_PMID(1,METRIC_ib_port_total_errors_link), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.recover */ + { (void *)IBPMDA_LINK_RECOVERS, + {PMDA_PMID(1,METRIC_ib_port_total_errors_recover), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.integrity */ + { (void *)IBPMDA_ERR_LOCALINTEG, + {PMDA_PMID(1,METRIC_ib_port_total_errors_integrity), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.vl15 */ + { (void *)IBPMDA_VL15_DROPPED, + {PMDA_PMID(1,METRIC_ib_port_total_errors_vl15), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.overrun */ + { (void *)IBPMDA_ERR_EXCESS_OVR, + {PMDA_PMID(1,METRIC_ib_port_total_errors_overrun), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.total.errors.symbol */ + { (void *)IBPMDA_ERR_SYM, + {PMDA_PMID(1,METRIC_ib_port_total_errors_symbol), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.control.query_timeout */ + { NULL, + {PMDA_PMID(2,METRIC_ib_control_query_timeout), + PM_TYPE_32, IB_PORT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* infiniband.control.hiwat */ + { NULL, + {PMDA_PMID(2,METRIC_ib_control_hiwat), + PM_TYPE_U32, IB_CNT_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* infiniband.port.switch.in.bytes */ + { NULL, + {PMDA_PMID(3,METRIC_ib_port_switch_in_bytes), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + /* infiniband.port.switch.in.packets */ + { NULL, + {PMDA_PMID(3,METRIC_ib_port_switch_in_packets), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.switch.out.bytes */ + { NULL, + {PMDA_PMID(3,METRIC_ib_port_switch_out_bytes), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + /* infiniband.port.switch.out.packets */ + { NULL, + {PMDA_PMID(3,METRIC_ib_port_switch_out_packets), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, + + /* infiniband.port.switch.total.bytes */ + { NULL, + {PMDA_PMID(3,METRIC_ib_port_switch_total_bytes), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + /* infiniband.port.switch.total.packets */ + { NULL, + {PMDA_PMID(3,METRIC_ib_port_switch_total_packets), + PM_TYPE_U64, IB_PORT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,0) } }, +}; + +pmdaIndom indomtab[] = { + { IB_HCA_INDOM, 0, NULL }, + { IB_PORT_INDOM, 0, NULL }, + { IB_CNT_INDOM, 0, NULL }, +}; + +static void +foreach_inst(pmInDom indom, void (*cb)(void *state)) +{ + int i; + pmdaCacheOp (indom, PMDA_CACHE_WALK_REWIND); + + while ((i = pmdaCacheOp (indom, PMDA_CACHE_WALK_NEXT)) >= 0) { + void *state = NULL; + + if (pmdaCacheLookup (indom, i, NULL, &state) != PMDA_CACHE_ACTIVE) { + abort(); + } + cb (state); + } +} + +int +ib_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int rv; + + foreach_inst (indomtab[IB_PORT_INDOM].it_indom, ib_rearm_for_update); + rv = pmdaFetch (numpmid, pmidlist, resp, pmda); + foreach_inst (indomtab[IB_PORT_INDOM].it_indom, ib_reset_perfcounters); + + return (rv); +} + +void +ibpmda_init(const char *confpath, int writeconf, pmdaInterface *dp) +{ + char defconf[MAXPATHLEN]; + int sep = __pmPathSeparator(); + int i; + + if (dp->status != 0) + return; + + if (confpath == NULL) { + snprintf(defconf, sizeof(defconf), "%s%c" "infiniband" "%c" "config", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + confpath = defconf; + } + + for (i=0; i < ARRAYSZ(indomtab); i++) { + __pmindom_int(&indomtab[i].it_indom)->domain = dp->domain; + if (IB_CNT_INDOM != __pmindom_int(&indomtab[i].it_indom)->serial) { + pmdaCacheOp (indomtab[i].it_indom, PMDA_CACHE_LOAD); + } + } + + + if ((dp->status = ib_load_config(confpath, writeconf, indomtab, ARRAYSZ(indomtab)))) + return; + + for (i=0; i < ARRAYSZ(indomtab); i++) { + if (IB_CNT_INDOM != __pmindom_int(&indomtab[i].it_indom)->serial) { + pmdaCacheOp (indomtab[i].it_indom, PMDA_CACHE_SAVE); + } + } + + dp->version.two.fetch = ib_fetch; + dp->version.two.store = ib_store; + pmdaSetFetchCallBack(dp, ib_fetch_val); + + pmdaInit(dp, indomtab, ARRAYSZ(indomtab), metrictab, ARRAYSZ(metrictab)); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n\n", pmProgname); + fputs("Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -c path to configuration file\n" + " -w write the basic configuration file\n", + stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int err = 0; + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + char *confpath = NULL; + int opt; + int writeconf = 0; + + __pmSetProgname(argv[0]); + snprintf(helppath, sizeof(helppath), "%s%c" "infiniband" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, IB, "infiniband.log", helppath); + + while ((opt = pmdaGetOpt(argc, argv, "D:c:d:l:w?", &dispatch, &err)) != EOF) { + switch (opt) { + case 'c': + confpath = optarg; + break; + case 'w': + writeconf = 1; + break; + default: + err++; + } + } + + if (err) { + usage(); + } + + if (!writeconf) { + /* If writeconf is specified, then errors should go to stdout + * since the PMDA daemon will exit immediately after writing + * out the default config file + */ + pmdaOpenLog(&dispatch); + } + ibpmda_init(confpath, writeconf, &dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/infiniband/pmns b/src/pmdas/infiniband/pmns new file mode 100644 index 0000000..b42919d --- /dev/null +++ b/src/pmdas/infiniband/pmns @@ -0,0 +1,100 @@ + +infiniband { + hca + port + control +} + +infiniband.hca { + type IB:0:0 + ca_type IB:0:1 + numports IB:0:2 + fw_ver IB:0:3 + hw_ver IB:0:4 + node_guid IB:0:5 + system_guid IB:0:6 +} + +infiniband.port { + guid IB:0:7 + gid_prefix IB:0:8 + lid IB:0:9 + state IB:0:10 + phystate IB:0:11 + rate IB:0:12 + capabilities IB:0:13 + linkspeed IB:0:14 + linkwidth IB:0:15 + + in + out + total + switch +} + +infiniband.port.in { + bytes IB:1:0 + packets IB:1:1 + errors +} + +infiniband.port.in.errors { + drop IB:1:4 + filter IB:1:9 + local IB:1:10 + remote IB:1:11 +} + +infiniband.port.out { + bytes IB:1:2 + packets IB:1:3 + errors +} + +infiniband.port.out.errors { + drop IB:1:5 + filter IB:1:12 +} + +infiniband.port.total { + bytes IB:1:6 + packets IB:1:7 + errors +} + +infiniband.port.total.errors { + drop IB:1:8 + filter IB:1:13 + link IB:1:14 + recover IB:1:15 + integrity IB:1:16 + vl15 IB:1:17 + overrun IB:1:18 + symbol IB:1:19 +} + +infiniband.port.switch { + in + out + total +} + +infiniband.port.switch.in { + bytes IB:3:0 + packets IB:3:1 +} + +infiniband.port.switch.out { + bytes IB:3:2 + packets IB:3:3 +} + +infiniband.port.switch.total { + bytes IB:3:4 + packets IB:3:5 +} + +infiniband.control { + query_timeout IB:2:0 + hiwat IB:2:1 +} diff --git a/src/pmdas/infiniband/root b/src/pmdas/infiniband/root new file mode 100644 index 0000000..460798c --- /dev/null +++ b/src/pmdas/infiniband/root @@ -0,0 +1,9 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { infiniband } + +#include "pmns" diff --git a/src/pmdas/jbd2/GNUmakefile b/src/pmdas/jbd2/GNUmakefile new file mode 100644 index 0000000..ac19900 --- /dev/null +++ b/src/pmdas/jbd2/GNUmakefile @@ -0,0 +1,73 @@ +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = jbd2 +DOMAIN = JBD2 +CMDTARGET = pmdajbd2 +LIBTARGET = pmda_jbd2.so +PMDAINIT = jbd2_init +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "jbd2 122 dso $(PMDAINIT) $(PMDADIR)/$(LIBTARGET)" + +CFILES = proc_jbd2.c pmda.c +HFILES = proc_jbd2.h convert.h +SCRIPTS = Install Remove +VERSION_SCRIPT = exports +HELPTARGETS = help.dir help.pag +LSRCFILES = help root root_jbd2 $(SCRIPTS) +LDIRT = $(HELPTARGETS) domain.h $(VERSION_SCRIPT) + +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(LIBTARGET) $(CMDTARGET) $(HELPTARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help $(HELPTARGETS) root root_jbd2 $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 root_jbd2 $(PCP_VAR_DIR)/pmns/root_jbd2 +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +$(HELPTARGETS) : help + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_jbd2 -v 2 -o help < help + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +pmda.o: domain.h +pmda.o proc_jbd2.o: convert.h +pmda.o proc_jbd2.o: proc_jbd2.h +pmda.o: $(VERSION_SCRIPT) diff --git a/src/pmdas/jbd2/Install b/src/pmdas/jbd2/Install new file mode 100755 index 0000000..4b1daea --- /dev/null +++ b/src/pmdas/jbd2/Install @@ -0,0 +1,31 @@ +#!/bin/sh +# +# Copyright (c) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Linux JBD2 PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=jbd2 +pmda_interface=4 +forced_restart=false +daemon_opt=true +pipe_opt=true +dso_opt=true +pmns_source=root_jbd2 + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/jbd2/Remove b/src/pmdas/jbd2/Remove new file mode 100755 index 0000000..9e1158f --- /dev/null +++ b/src/pmdas/jbd2/Remove @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright (c) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Linux JBD2 PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh +iam=jbd2 +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/jbd2/convert.h b/src/pmdas/jbd2/convert.h new file mode 100644 index 0000000..69fc51a --- /dev/null +++ b/src/pmdas/jbd2/convert.h @@ -0,0 +1,30 @@ +/* + * Size conversion for different sized types extracted from the + * kernel on different platforms, particularly where the sizeof + * "long" differs. + * + * Copyright (c) 2007-2008 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Some metrics are exported by the kernel as "unsigned long". + * On most 64bit platforms this is not the same size as an + * "unsigned int". + */ +#if defined(HAVE_64BIT_LONG) +#define KERNEL_ULONG PM_TYPE_U64 +#define _pm_assign_ulong(atomp, val) do { (atomp)->ull = (val); } while (0) +#else +#define KERNEL_ULONG PM_TYPE_U32 +#define _pm_assign_ulong(atomp, val) do { (atomp)->ul = (val); } while (0) +#endif diff --git a/src/pmdas/jbd2/help b/src/pmdas/jbd2/help new file mode 100644 index 0000000..9cfada7 --- /dev/null +++ b/src/pmdas/jbd2/help @@ -0,0 +1,115 @@ +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# JBD2 PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ jbd2.njournals Count of active JBD2 (Journal Block Device v2) devices + +@ jbd2.transaction.count Total transactions committed per journal +This metric is sourced from the per-device /proc/fs/jbd2 info file. + +@ jbd2.transaction.requested Total journal transactions requested per journal +This metric is sourced from the per-device /proc/fs/jbd2 info file. + +@ jbd2.transaction.max_blocks Maximum transaction blocks (buffers) per journal +This metric is sourced from the per-device /proc/fs/jbd2 info file. + +@ jbd2.transaction.average.time.waiting Average time waiting per journal +Average time spent waiting for transactions to complete since mount. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.time.request_delay Average request delay per journal +Average request delay for all transactions to complete since mount. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.time.running Average running time per journal +Average transaction running time over all transactions since mount. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.time.being_locked Average locked time per journal +Average transaction locked time over all transactions since mount. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.time.flushing_ordered_mode_data Average data flush time per journal +Average time flushing data (ordered mode) for all transactions since +mount. Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.time.logging Average logging time per journal +Average time spent logging transactions for all transactions since +mount. Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.time.committing Average commit time per journal +Average time spent committing transactions for all transactions since +mount. Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.blocks Average transaction blocks per journal +Average number of blocks per transaction for all transactions. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.blocks_logged Average logged blocks per journal +Average number of blocks logged per transaction for all transactions. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.average.handles Average handle count per journal +Average number of handles used per transaction for all transactions. +Exported directly from per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.time.waiting Total time waiting per journal +Total time spent waiting for transactions to complete since mount. +Derived from values in the per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.time.request_delay Total request delay per journal +Total request delay for all transactions to complete since mount. +Derived from values in the per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.time.running Total running time per journal +Total transaction running time over all transactions since mount. +Derived from values in the per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.time.being_locked Total locked time per journal +Total transaction locked time over all transactions since mount. +Derived from values in the per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.time.flushing_ordered_mode_data Total data flush time per journal +Total time flushing data (ordered mode) for all transactions since +mount. Derived from values in per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.time.logging Total logging time per journal +Total time spent logging transactions for all transactions since +mount. Derived from values in per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.blocks Total transaction blocks per journal +Total number of blocks in all transactions since device mounted. +Derived from values in the per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.blocks_logged Total logged blocks per journal +Total number of blocks logged in all transactions since mount. +Derived from values in the per-device /proc/fs/jbd2 info files. + +@ jbd2.transaction.total.handles Total handle count per journal +Total count of handles used in all transactions since mount. +Derived from values in the per-device /proc/fs/jbd2 info files. diff --git a/src/pmdas/jbd2/pmda.c b/src/pmdas/jbd2/pmda.c new file mode 100644 index 0000000..3c2dcba --- /dev/null +++ b/src/pmdas/jbd2/pmda.c @@ -0,0 +1,322 @@ +/* + * Journal Block Device v2 PMDA + * + * Copyright (c) 2013-2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "convert.h" +#include "proc_jbd2.h" + +static int _isDSO = 1; /* =0 I am a daemon */ +static char *prefix = "/proc/fs/jbd2"; +static char *username; + +#define JBD2_INDOM 0 +#define INDOM(i) (indomtab[i].it_indom) +static pmdaIndom indomtab[] = { + { JBD2_INDOM, 0, NULL }, /* cached */ +}; + +static pmdaMetric metrictab[] = { + +/* jbd2.njournals */ + { NULL, { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* jbd2.transaction.count */ + { NULL, { PMDA_PMID(0,1), KERNEL_ULONG, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* jbd2.transaction.requested */ + { NULL, { PMDA_PMID(0,2), KERNEL_ULONG, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* jbd2.transaction.max_blocks */ + { NULL, { PMDA_PMID(0,3), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* jbd2.transaction.total.time.waiting */ + { NULL, { PMDA_PMID(0,4), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.total.time.request_delay */ + { NULL, { PMDA_PMID(0,5), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.total.time.running */ + { NULL, { PMDA_PMID(0,6), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.total.time.being_locked */ + { NULL, { PMDA_PMID(0,7), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.total.time.flushing_ordered_mode_data */ + { NULL, { PMDA_PMID(0,8), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.total.time.logging */ + { NULL, { PMDA_PMID(0,9), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.total.blocks */ + { NULL, { PMDA_PMID(0,10), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* jbd2.transaction.total.blocks_logged */ + { NULL, { PMDA_PMID(0,11), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* jbd2.transaction.total.handles */ + { NULL, { PMDA_PMID(0,12), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* jbd2.transaction.average.time.waiting */ + { NULL, { PMDA_PMID(0,13), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.average.time.request_delay */ + { NULL, { PMDA_PMID(0,14), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.average.time.running */ + { NULL, { PMDA_PMID(0,15), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.average.time.being_locked */ + { NULL, { PMDA_PMID(0,16), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.average.time.flushing_ordered_mode_data */ + { NULL, { PMDA_PMID(0,17), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.average.time.logging */ + { NULL, { PMDA_PMID(0,18), PM_TYPE_U32, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +/* jbd2.transaction.average.time.committing */ + { NULL, { PMDA_PMID(0,19), PM_TYPE_U64, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) }, }, +/* jbd2.transaction.average.blocks */ + { NULL, { PMDA_PMID(0,20), KERNEL_ULONG, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* jbd2.transaction.average.blocks_logged */ + { NULL, { PMDA_PMID(0,21), KERNEL_ULONG, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* jbd2.transaction.average.handles */ + { NULL, { PMDA_PMID(0,22), KERNEL_ULONG, JBD2_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +}; + +static int +jbd2_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + refresh_jbd2(prefix, INDOM(JBD2_INDOM)); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +jbd2_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + refresh_jbd2(prefix, INDOM(JBD2_INDOM)); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +jbd2_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int sts; + proc_jbd2_t *jbd2; + + switch (idp->cluster) { + case 0: + if (!idp->item) { /* jbd2.njournals */ + atom->ul = pmdaCacheOp(INDOM(JBD2_INDOM), PMDA_CACHE_SIZE_ACTIVE); + break; + } + + /* lookup the instance (journal device) */ + sts = pmdaCacheLookup(INDOM(JBD2_INDOM), inst, NULL, (void **)&jbd2); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + if (jbd2->version < 2) + return 0; + + switch (idp->item) { + + case 1: /* transaction.count */ + _pm_assign_ulong(atom, jbd2->tid); + break; + case 2: /* transaction.requested */ + if (jbd2->version < 3) + return 0; + _pm_assign_ulong(atom, jbd2->requested); + break; + case 3: /* transaction.max_blocks */ + atom->ul = jbd2->max_buffers; + break; + + case 4: /* transaction.total.time.waiting */ + atom->ull = jbd2->waiting * jbd2->tid; + break; + case 5: /* transaction.total.time.request_delay */ + if (jbd2->version < 3) + return 0; + atom->ull = jbd2->request_delay * jbd2->requested; + break; + case 6: /* transaction.total.time.running */ + atom->ull = jbd2->running * jbd2->tid; + break; + case 7: /* transaction.total.time.being_locked */ + atom->ull = jbd2->locked * jbd2->tid; + break; + case 8: /* transaction.total.time.flushing_ordered_mode_data */ + atom->ull = jbd2->flushing * jbd2->tid; + break; + case 9: /* transaction.total.time.logging */ + atom->ull = jbd2->logging * jbd2->tid; + break; + case 10: /* transaction.total.blocks */ + atom->ull = jbd2->blocks * jbd2->tid; + break; + case 11: /* transaction.total.blocks_logged */ + atom->ull = jbd2->blocks_logged * jbd2->tid; + break; + case 12: /* transaction.total.handles */ + atom->ull = jbd2->handles * jbd2->tid; + break; + + case 13: /* transaction.average.time.waiting */ + atom->ul = jbd2->waiting; + break; + case 14: /* transaction.total.time.request_delay */ + if (jbd2->version < 3) + return 0; + atom->ul = jbd2->request_delay; + break; + case 15: /* transaction.total.time.running */ + atom->ul = jbd2->running; + break; + case 16: /* transaction.total.time.being_locked */ + atom->ul = jbd2->locked; + break; + case 17: /* transaction.total.time.flushing_ordered_mode_data */ + atom->ul = jbd2->flushing; + break; + case 18: /* transaction.total.time.logging */ + atom->ul = jbd2->logging; + break; + case 19: /* transaction.average.time.committing */ + atom->ull = jbd2->average_commit_time; + break; + case 20: /* transaction.total.blocks */ + _pm_assign_ulong(atom, jbd2->blocks); + break; + case 21: /* transaction.total.blocks_logged */ + _pm_assign_ulong(atom, jbd2->blocks_logged); + break; + case 22: /* transaction.total.handles */ + _pm_assign_ulong(atom, jbd2->handles); + break; + + default: + return PM_ERR_PMID; + } + break; + + default: /* unknown cluster */ + return PM_ERR_PMID; + } + + return 1; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +__PMDA_INIT_CALL +jbd2_init(pmdaInterface *dp) +{ + size_t nmetrics, nindoms; + + if (_isDSO) { + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(helppath, sizeof(helppath), "%s%c" "jbd2" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_4, "jbd2 DSO", helppath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.four.instance = jbd2_instance; + dp->version.four.fetch = jbd2_fetch; + pmdaSetFetchCallBack(dp, jbd2_fetchCallBack); + + nindoms = sizeof(indomtab)/sizeof(indomtab[0]); + nmetrics = sizeof(metrictab)/sizeof(metrictab[0]); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_DIRECT); + pmdaInit(dp, indomtab, nindoms, metrictab, nmetrics); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + { "", 1, 'j', "PATH", "path to stats files (default \"/proc/fs/jbd2\")" }, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:j:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int c, sep = __pmPathSeparator(); + pmdaInterface dispatch; + char help[MAXPATHLEN]; + + _isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(help, sizeof(help), "%s%c" "jbd2" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, JBD2, "jbd2.log", help); + + while ((c = pmdaGetOptions(argc, argv, &opts, &dispatch)) != EOF) { + switch(c) { + case 'j': + prefix = opts.optarg; + break; + } + } + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + jbd2_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/jbd2/proc_jbd2.c b/src/pmdas/jbd2/proc_jbd2.c new file mode 100644 index 0000000..2a2692c --- /dev/null +++ b/src/pmdas/jbd2/proc_jbd2.c @@ -0,0 +1,148 @@ +/* + * Linux JBD2 (ext3/ext4) driver metrics. + * + * Copyright (C) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "pmapi.h" +#include "pmda.h" +#include "proc_jbd2.h" + +enum { + HEADER_STATS, + SEEKING_STATS, + AVERAGE_STATS, +}; + +static int +refresh_journal(const char *path, const char *dev, pmInDom indom) +{ + int n, state, indom_changed = 0; + char buf[MAXPATHLEN], *id; + unsigned long long value; + proc_jbd2_t *jp; + FILE *fp; + + if (dev[0] == '.') + return 0; /* only interest is in device files */ + if (snprintf(buf, sizeof(buf), "%s/%s/info", path, dev) == sizeof(buf)) + return 0; /* ignore, dodgey command line args */ + if ((fp = fopen(buf, "r")) == NULL) + return 0; /* no permission, ignore this entry */ + + if (pmdaCacheLookupName(indom, dev, &n, (void **)&jp) < 0 || !jp) { + if ((jp = (proc_jbd2_t *)calloc(1, sizeof(proc_jbd2_t))) != NULL) + indom_changed++; + } + if (!jp) { + fclose(fp); + return 0; + } + + state = HEADER_STATS; /* seeking the header, initially */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + switch (state) { + case HEADER_STATS: + if (sscanf(buf, + "%llu transactions (%llu requested), each up to %u blocks\n", + (unsigned long long *) &jp->tid, + (unsigned long long *) &jp->requested, + (unsigned int *) &jp->max_buffers) == 3) { + state = SEEKING_STATS; + jp->version = 3; /* 3.x kernel header format */ + } + else if (sscanf(buf, + "%llu transaction, each up to %u blocks\n", + (unsigned long long *) &jp->tid, + (unsigned int *) &jp->max_buffers) == 2) { + state = SEEKING_STATS; + jp->version = 2; /* 2.x kernel header format */ + } + break; + + case SEEKING_STATS: + if (strncmp(buf, "average: \n", 8) == 0) + state = AVERAGE_STATS; + break; + + case AVERAGE_STATS: + value = strtoull(buf, &id, 10); + if (id == buf) + continue; + else if (strcmp(id, "ms waiting for transaction\n") == 0) + jp->waiting = value; + else if (strcmp(id, "ms request delay\n") == 0) + jp->request_delay = value; + else if (strcmp(id, "ms running transaction\n") == 0) + jp->running = value; + else if (strcmp(id, "ms transaction was being locked\n") == 0) + jp->locked = value; + else if (strcmp(id, "ms flushing data (in ordered mode)\n") == 0) + jp->flushing = value; + else if (strcmp(id, "ms logging transaction\n") == 0) + jp->logging = value; + else if (strcmp(id, "us average transaction commit time\n") == 0) + jp->average_commit_time = value; + else if (strcmp(id, " handles per transaction\n") == 0) + jp->handles = value; + else if (strcmp(id, " blocks per transaction\n") == 0) + jp->blocks = value; + else if (strcmp(id, " logged blocks per transaction\n") == 0) + jp->blocks_logged = value; + break; + + default: + break; + } + } + fclose(fp); + + if (state != AVERAGE_STATS) { + if (indom_changed) + free(jp); + return 0; + } + + pmdaCacheStore(indom, PMDA_CACHE_ADD, dev, jp); + return indom_changed; +} + +int +refresh_jbd2(const char *path, pmInDom jbd2_indom) +{ + DIR *dirp; + struct dirent *dent; + int indom_changes = 0; + static int first = 1; + + if (first) { + /* initialize the instance domain caches */ + pmdaCacheOp(jbd2_indom, PMDA_CACHE_LOAD); + indom_changes = 1; + first = 0; + } + + pmdaCacheOp(jbd2_indom, PMDA_CACHE_INACTIVE); + if ((dirp = opendir(path)) == NULL) + return -ENOENT; + while ((dent = readdir(dirp)) != NULL) + indom_changes |= refresh_journal(path, dent->d_name, jbd2_indom); + closedir(dirp); + + if (indom_changes) + pmdaCacheOp(jbd2_indom, PMDA_CACHE_SAVE); + return 0; +} diff --git a/src/pmdas/jbd2/proc_jbd2.h b/src/pmdas/jbd2/proc_jbd2.h new file mode 100644 index 0000000..fb9c825 --- /dev/null +++ b/src/pmdas/jbd2/proc_jbd2.h @@ -0,0 +1,38 @@ +/* + * Linux JBD2 (ext3/ext4) driver metrics. + * + * Copyright (C) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +typedef struct { + __uint32_t version; + __uint32_t max_buffers; + + __uint64_t tid; + __uint64_t requested; + + __uint64_t waiting; + __uint64_t request_delay; + __uint64_t running; + __uint64_t locked; + __uint64_t flushing; + __uint64_t logging; + __uint64_t average_commit_time; + + __uint64_t handles; + + __uint64_t blocks; + __uint64_t blocks_logged; +} proc_jbd2_t; + +extern int refresh_jbd2(const char *path, pmInDom jbd2_indom); diff --git a/src/pmdas/jbd2/root b/src/pmdas/jbd2/root new file mode 100644 index 0000000..12878cb --- /dev/null +++ b/src/pmdas/jbd2/root @@ -0,0 +1,6 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include +#include "root_jbd2" diff --git a/src/pmdas/jbd2/root_jbd2 b/src/pmdas/jbd2/root_jbd2 new file mode 100644 index 0000000..7cb10cd --- /dev/null +++ b/src/pmdas/jbd2/root_jbd2 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +root { + jbd2 +} + +jbd2 { + njournals 122:0:0 + transaction +} + +jbd2.transaction { + count 122:0:1 + requested 122:0:2 + max_blocks 122:0:3 + total + average +} + +jbd2.transaction.total { + time + blocks 122:0:10 + blocks_logged 122:0:11 + handles 122:0:12 +} + +jbd2.transaction.total.time { + waiting 122:0:4 + request_delay 122:0:5 + running 122:0:6 + being_locked 122:0:7 + flushing_ordered_mode_data 122:0:8 + logging 122:0:9 +} + +jbd2.transaction.average { + time + blocks 122:0:20 + blocks_logged 122:0:21 + handles 122:0:22 +} + +jbd2.transaction.average.time { + waiting 122:0:13 + request_delay 122:0:14 + running 122:0:15 + being_locked 122:0:16 + flushing_ordered_mode_data 122:0:17 + logging 122:0:18 + committing 122:0:19 +} diff --git a/src/pmdas/kvm/GNUmakefile b/src/pmdas/kvm/GNUmakefile new file mode 100644 index 0000000..6fd0b6a --- /dev/null +++ b/src/pmdas/kvm/GNUmakefile @@ -0,0 +1,56 @@ +#!gmake +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = kvm +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) +else +install: +endif + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/kvm/Install b/src/pmdas/kvm/Install new file mode 100755 index 0000000..3317662 --- /dev/null +++ b/src/pmdas/kvm/Install @@ -0,0 +1,40 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the KVM PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=kvm +perl_opt=true +daemon_opt=false +forced_restart=true + +if ! test -d /sys/kernel/debug; then + echo "DEBUGFS not enabled in your kernel" + exit 1 +fi +if ! test -d /sys/kernel/debug/kvm; then + echo "KVM statistics unavailable (load kvm module and mount debugfs)" + exit 1 +fi +if ! test -r /sys/kernel/debug/kvm; then + forced_restart=true +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/kvm/Remove b/src/pmdas/kvm/Remove new file mode 100755 index 0000000..1032be0 --- /dev/null +++ b/src/pmdas/kvm/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the KVM PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=kvm + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/kvm/pmdakvm.pl b/src/pmdas/kvm/pmdakvm.pl new file mode 100644 index 0000000..6eb7419 --- /dev/null +++ b/src/pmdas/kvm/pmdakvm.pl @@ -0,0 +1,122 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +use vars qw( $pmda ); +my $kvm_path = '/sys/kernel/debug/kvm'; + +sub kvm_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ($path, $value, $fh); + + # $pmda->log("kvm_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + if (!defined($metric_name)) { return (PM_ERR_PMID, 0); } + $path = $metric_name; + $path =~ s/^kvm\./$kvm_path\//; + open($fh, $path) || return (PM_ERR_APPVERSION, 0); + $value = <$fh>; + close $fh; + + if (!defined($value)) { return (PM_ERR_APPVERSION, 0); } + return ($value, 1); +} + +$pmda = PCP::PMDA->new('kvm', 95); +$pmda->connect_pmcd; + +# May need to be root to read the directory $kvm_path (/sys/kernel/debug/kvm) +# and so +# (a) do not use $pmda->set_user('pcp') below, and +# (b) need forced_restart=true in the Install script so pmcd is restarted +# and we're running as root at this point (SIGHUP pmcd once it has +# changed to user "pcp" is not going to work for PMDA installation) +# +my $pmid = 0; +opendir(DIR, $kvm_path) || $pmda->err("pmdakvm failed to open $kvm_path: $!"); +my @metrics = grep { + unless (/^\./) { + $pmda->add_metric(pmda_pmid(0,$pmid++), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "kvm.$_", '', ''); + # $pmda->log("pmdakvm added metric kvm.$_\n"); + } +} readdir(DIR); +closedir DIR; + +$pmda->set_fetch_callback(\&kvm_fetch_callback); +# Careful with permissions - may need to be root to read /sys/kernel/debug/kvm +# see note above. +#$pmda->set_user('pcp') if -r $kvm_path; +$pmda->run; + +=pod + +=head1 NAME + +pmdakvm - Linux virtualisation performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from the Linux KVM virtualisation subsystem. + +Unlike many PMDAs it dynamically enumerates its metric hierarchy, +based entirely on the contents of /sys/kernel/debug/kvm. + +=head1 INSTALLATION + +If you want access to the names and values for the kvm performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/kvm + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/kvm + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/kvm/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/kvm/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/kvm.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and kvm(1). diff --git a/src/pmdas/linux/GNUmakefile b/src/pmdas/linux/GNUmakefile new file mode 100644 index 0000000..4a0cc4f --- /dev/null +++ b/src/pmdas/linux/GNUmakefile @@ -0,0 +1,128 @@ +# +# Copyright (c) 2000,2003,2004,2008 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2007-2010 Aconex. All Rights Reserved. +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = linux +DOMAIN = LINUX +CMDTARGET = pmdalinux +LIBTARGET = pmda_linux.so +PMDAINIT = linux_init +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LOGREWRITEDIR = $(PCP_VAR_DIR)/config/pmlogrewrite +CONF_LINE = "linux 60 dso $(PMDAINIT) $(PMDADIR)/$(LIBTARGET)" + +CFILES = pmda.c \ + proc_stat.c proc_meminfo.c proc_loadavg.c \ + proc_net_dev.c interrupts.c filesys.c \ + swapdev.c devmapper.c proc_net_rpc.c proc_partitions.c \ + getinfo.c proc_net_sockstat.c proc_net_snmp.c \ + proc_scsi.c proc_cpuinfo.c proc_net_tcp.c \ + proc_slabinfo.c sem_limits.c msg_limits.c shm_limits.c \ + proc_uptime.c proc_sys_fs.c proc_vmstat.c \ + sysfs_kernel.c linux_table.c numa_meminfo.c \ + proc_net_netstat.c + +HFILES = clusters.h indom.h convert.h \ + proc_stat.h proc_meminfo.h proc_loadavg.h \ + proc_net_dev.h interrupts.h filesys.h \ + swapdev.h devmapper.h proc_net_rpc.h proc_partitions.h \ + getinfo.h proc_net_sockstat.h proc_net_snmp.h \ + proc_scsi.h proc_cpuinfo.h proc_net_tcp.h \ + proc_slabinfo.h sem_limits.h msg_limits.h shm_limits.h \ + proc_uptime.h proc_sys_fs.h proc_vmstat.h \ + sysfs_kernel.h linux_table.h numa_meminfo.h \ + proc_net_netstat.h + +VERSION_SCRIPT = exports +HELPTARGETS = help.dir help.pag +LSRCFILES = help root_linux proc_net_snmp_migrate.conf +LDIRT = $(HELPTARGETS) domain.h $(VERSION_SCRIPT) + +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) + +# Uncomment these flags for profiling +# LCFLAGS += -pg +# LLDFLAGS += -pg + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(LIBTARGET) $(CMDTARGET) $(HELPTARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help $(HELPTARGETS) $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root_linux $(PCP_VAR_DIR)/pmns/root_linux + $(INSTALL) -m 644 proc_net_snmp_migrate.conf $(LOGREWRITEDIR)/linux_proc_net_snmp_migrate.conf +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +$(HELPTARGETS) : help + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_linux -v 2 -o help < help + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +interrupts.o pmda.o proc_partitions.o: clusters.h +pmda.o proc_partitions.o: convert.h +pmda.o: domain.h +filesys.o interrupts.o pmda.o: filesys.h +pmda.o: getinfo.h +pmda.o devmapper.o: devmapper.h +numa_meminfo.o pmda.o proc_cpuinfo.o proc_partitions.o proc_stat.o: indom.h +interrupts.o pmda.o: interrupts.h +linux_table.o numa_meminfo.o pmda.o: linux_table.h +msg_limits.o pmda.o: msg_limits.h +numa_meminfo.o pmda.o: numa_meminfo.h +pmda.o proc_cpuinfo.o proc_stat.o: proc_cpuinfo.h +pmda.o proc_loadavg.o: proc_loadavg.h +pmda.o proc_meminfo.o: proc_meminfo.h +pmda.o proc_net_dev.o: proc_net_dev.h +pmda.o proc_net_netstat.o: proc_net_netstat.h +pmda.o proc_net_rpc.o: proc_net_rpc.h +pmda.o proc_net_snmp.o: proc_net_snmp.h +pmda.o proc_net_sockstat.o: proc_net_sockstat.h +pmda.o proc_net_tcp.o: proc_net_tcp.h +pmda.o proc_partitions.o: proc_partitions.h +pmda.o proc_scsi.o: proc_scsi.h +pmda.o proc_slabinfo.o: proc_slabinfo.h +pmda.o proc_stat.o: proc_stat.h +pmda.o proc_sys_fs.o: proc_sys_fs.h +pmda.o proc_uptime.o: proc_uptime.h +pmda.o proc_vmstat.o: proc_vmstat.h +pmda.o sem_limits.o: sem_limits.h +pmda.o shm_limits.o: shm_limits.h +pmda.o swapdev.o: swapdev.h +pmda.o sysfs_kernel.o: sysfs_kernel.h +pmda.o: $(VERSION_SCRIPT) diff --git a/src/pmdas/linux/clusters.h b/src/pmdas/linux/clusters.h new file mode 100644 index 0000000..0a1fdfe --- /dev/null +++ b/src/pmdas/linux/clusters.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2005,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _CLUSTERS_H +#define _CLUSTERS_H + +/* + * fetch cluster numbers + */ +enum { + CLUSTER_STAT = 0, /* 0 /proc/stat */ + CLUSTER_MEMINFO, /* 1 /proc/meminfo */ + CLUSTER_LOADAVG, /* 2 /proc/loadavg */ + CLUSTER_NET_DEV, /* 3 /proc/net/dev */ + CLUSTER_INTERRUPTS, /* 4 /proc/interrupts */ + CLUSTER_FILESYS, /* 5 /proc/mounts + statfs */ + CLUSTER_SWAPDEV, /* 6 /proc/swaps */ + CLUSTER_NET_NFS, /* 7 /proc/net/rpc/nfs + /proc/net/rpc/nfsd */ + PROC_PID_STAT, /* 8 /proc//stat -> proc PMDA */ + PROC_PID_STATM, /* 9 /proc//statm + /proc//maps -> proc PMDA */ + CLUSTER_PARTITIONS, /* 10 /proc/partitions */ + CLUSTER_NET_SOCKSTAT, /* 11 /proc/net/sockstat */ + CLUSTER_KERNEL_UNAME, /* 12 uname() system call */ + PROC_PROC_RUNQ, /* 13 number of processes in various states -> proc PMDA */ + CLUSTER_NET_SNMP, /* 14 /proc/net/snmp */ + CLUSTER_SCSI, /* 15 /proc/scsi/scsi */ + CLUSTER_XFS, /* 16 /proc/fs/xfs/stat -> xfs PMDA */ + CLUSTER_XFSBUF, /* 17 /proc/fs/pagebuf/stat -> xfs PMDA */ + CLUSTER_CPUINFO, /* 18 /proc/cpuinfo */ + CLUSTER_NET_TCP, /* 19 /proc/net/tcp */ + CLUSTER_SLAB, /* 20 /proc/slabinfo */ + CLUSTER_SEM_LIMITS, /* 21 semctl(IPC_INFO) system call */ + CLUSTER_MSG_LIMITS, /* 22 msgctl(IPC_INFO) system call */ + CLUSTER_SHM_LIMITS, /* 23 shmctl(IPC_INFO) system call */ + PROC_PID_STATUS, /* 24 /proc//status -> proc PMDA */ + CLUSTER_NUSERS, /* 25 number of users */ + CLUSTER_UPTIME, /* 26 /proc/uptime */ + CLUSTER_VFS, /* 27 /proc/sys/fs */ + CLUSTER_VMSTAT, /* 28 /proc/vmstat */ + CLUSTER_IB, /* deprecated: do not re-use 29 infiniband */ + CLUSTER_QUOTA, /* 30 quotactl() -> xfs PMDA */ + PROC_PID_SCHEDSTAT, /* 31 /proc//schedstat -> proc PMDA */ + PROC_PID_IO, /* 32 /proc//io -> proc PMDA */ + CLUSTER_NET_ADDR, /* 33 /proc/net/dev and ioctl(SIOCGIFCONF) */ + CLUSTER_TMPFS, /* 34 /proc/mounts + statfs (tmpfs only) */ + CLUSTER_SYSFS_KERNEL, /* 35 /sys/kernel metrics */ + CLUSTER_NUMA_MEMINFO, /* 36 /sys/devices/system/node* NUMA memory */ + PROC_CGROUP_SUBSYS, /* 37 /proc/cgroups control group subsystems -> proc PMDA */ + PROC_CGROUP_MOUNTS, /* 38 /proc/mounts active control groups -> proc PMDA */ + PROC_CPUSET_GROUPS, /* 39 cpuset control groups -> proc PMDA */ + PROC_CPUSET_PROCS, /* 40 cpuset control group processes -> proc PMDA */ + PROC_CPUACCT_GROUPS, /* 41 cpu accounting control groups -> proc PMDA */ + PROC_CPUACCT_PROCS, /* 42 cpu accounting group processes -> proc PMDA */ + PROC_CPUSCHED_GROUPS, /* 43 scheduler control groups -> proc PMDA */ + PROC_CPUSCHED_PROCS, /* 44 scheduler group processes -> proc PMDA */ + PROC_MEMORY_GROUPS, /* 45 memory control groups -> proc PMDA */ + PROC_MEMORY_PROCS, /* 46 memory group processes -> proc PMDA */ + PROC_NET_CLS_GROUPS, /* 47 network classification control groups -> proc PMDA */ + PROC_NET_CLS_PROCS, /* 48 network classification group processes -> proc PMDA */ + CLUSTER_INTERRUPT_LINES,/* 49 /proc/interrupts percpu interrupts */ + CLUSTER_INTERRUPT_OTHER,/* 50 /proc/interrupts percpu interrupts */ + PROC_PID_FD, /* 51 /proc//fd -> proc PMDA */ + CLUSTER_LV, /* 52 /dev/mapper */ + CLUSTER_NET_NETSTAT, /* 53 /proc/net/netstat */ + CLUSTER_DM, /* 54 disk.dm.* */ + + NUM_CLUSTERS /* one more than highest numbered cluster */ +}; + +#endif /* _CLUSTERS_H */ diff --git a/src/pmdas/linux/convert.h b/src/pmdas/linux/convert.h new file mode 100644 index 0000000..8a31eda --- /dev/null +++ b/src/pmdas/linux/convert.h @@ -0,0 +1,49 @@ +/* + * Size conversion for different sized types extracted from the + * kernel on different platforms, particularly where the sizeof + * "long" differs. + * + * Copyright (c) 2007-2008 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Some metrics are exported by the kernel as "unsigned long". + * On most 64bit platforms this is not the same size as an + * "unsigned int". + */ +#if defined(HAVE_64BIT_LONG) +#define KERNEL_ULONG PM_TYPE_U64 +#define _pm_assign_ulong(atomp, val) do { (atomp)->ull = (val); } while (0) +#else +#define KERNEL_ULONG PM_TYPE_U32 +#define _pm_assign_ulong(atomp, val) do { (atomp)->ul = (val); } while (0) +#endif + +/* + * Some metrics need to have their type set at runtime, based on the + * running kernel version (not simply a 64 vs 32 bit machine issue). + */ +#define KERNEL_UTYPE PM_TYPE_NOSUPPORT /* set to real type at runtime */ +#define _pm_metric_type(type, size) \ + do { \ + (type) = ((size)==8 ? PM_TYPE_U64 : PM_TYPE_U32); \ + } while (0) +#define _pm_assign_utype(size, atomp, val) \ + do { \ + if ((size)==8) { (atomp)->ull = (val); } else { (atomp)->ul = (val); } \ + } while (0) + diff --git a/src/pmdas/linux/devmapper.c b/src/pmdas/linux/devmapper.c new file mode 100644 index 0000000..38f87d3 --- /dev/null +++ b/src/pmdas/linux/devmapper.c @@ -0,0 +1,86 @@ +/* + * Linux LVM Devices Cluster + * + * Copyright (c) 2013-2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "devmapper.h" + +int +refresh_dev_mapper(dev_mapper_t *lvs) +{ + int i; + DIR *dirp; + struct dirent *dentry; + struct stat statbuf; + char path[MAXPATHLEN]; + + snprintf(path, sizeof(path), "%s/dev/mapper", linux_statspath); + if ((dirp = opendir(path)) == NULL) + return 1; + + for (i = 0; i < lvs->nlv; i++) { + free(lvs->lv[i].dev_name); + free(lvs->lv[i].lv_name); + } + lvs->nlv = 0; + lvs->lv = NULL; + while ((dentry = readdir(dirp)) != NULL) { + char linkname[MAXPATHLEN]; + int linkname_len; + + snprintf(path, sizeof(path), + "%s/dev/mapper/%s", linux_statspath, dentry->d_name); + + if (stat(path, &statbuf) == -1) + continue; + if (!S_ISBLK(statbuf.st_mode)) + continue; + + if ((linkname_len = readlink(path, linkname, sizeof(linkname)-1)) < 0) + continue; + linkname[linkname_len] = '\0'; + + i = lvs->nlv; + lvs->nlv++; + + lvs->lv = (lv_entry_t *)realloc(lvs->lv, lvs->nlv * sizeof(lv_entry_t)); + lvs->lv[i].id = lvs->nlv; + + lvs->lv[i].dev_name = malloc(strlen(dentry->d_name)+1); + strcpy(lvs->lv[i].dev_name, dentry->d_name); + + lvs->lv[i].lv_name = malloc(linkname_len+1); + strcpy(lvs->lv[i].lv_name, linkname); + } + closedir(dirp); + + if (lvs->lv_indom->it_numinst != lvs->nlv) { + lvs->lv_indom->it_numinst = lvs->nlv; + lvs->lv_indom->it_set = (pmdaInstid *) + realloc(lvs->lv_indom->it_set, lvs->nlv * sizeof(pmdaInstid)); + } + for (i = 0; i < lvs->nlv; i++) { + int skip_prefix = 0; + lvs->lv_indom->it_set[i].i_inst = lvs->lv[i].id; + if (strncmp(lvs->lv[i].lv_name, "../", 3) == 0) + skip_prefix = 3; + lvs->lv_indom->it_set[i].i_name = lvs->lv[i].lv_name + skip_prefix; + } + return 0; +} diff --git a/src/pmdas/linux/devmapper.h b/src/pmdas/linux/devmapper.h new file mode 100644 index 0000000..7b75281 --- /dev/null +++ b/src/pmdas/linux/devmapper.h @@ -0,0 +1,29 @@ +/* + * Linux /dev/mapper metrics cluster + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +typedef struct { + int id; /* internal instance id */ + char *dev_name; + char *lv_name; +} lv_entry_t; + +typedef struct { + int nlv; + lv_entry_t *lv; + pmdaIndom *lv_indom; +} dev_mapper_t; + +extern int refresh_dev_mapper(dev_mapper_t *); diff --git a/src/pmdas/linux/filesys.c b/src/pmdas/linux/filesys.c new file mode 100644 index 0000000..3c06c13 --- /dev/null +++ b/src/pmdas/linux/filesys.c @@ -0,0 +1,122 @@ +/* + * Linux Filesystem Cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "filesys.h" + +char * +scan_filesys_options(const char *options, const char *option) +{ + static char buffer[128]; + char *s; + + strncpy(buffer, options, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + + s = strtok(buffer, ","); + while (s) { + if (strcmp(s, option) == 0) + return s; + s = strtok(NULL, ","); + } + return NULL; +} + +int +refresh_filesys(pmInDom filesys_indom, pmInDom tmpfs_indom) +{ + char buf[MAXPATHLEN]; + char realdevice[MAXPATHLEN]; + filesys_t *fs; + pmInDom indom; + FILE *fp; + char *path, *device, *type, *options; + int sts; + + pmdaCacheOp(tmpfs_indom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(filesys_indom, PMDA_CACHE_INACTIVE); + + if ((fp = linux_statsfile("/proc/mounts", buf, sizeof(buf))) == NULL) + return -oserror(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((device = strtok(buf, " ")) == 0) + continue; + + path = strtok(NULL, " "); + type = strtok(NULL, " "); + options = strtok(NULL, " "); + if (strcmp(type, "proc") == 0 || + strcmp(type, "nfs") == 0 || + strcmp(type, "devfs") == 0 || + strcmp(type, "devpts") == 0 || + strcmp(type, "cgroup") == 0 || + strncmp(type, "auto", 4) == 0) + continue; + + indom = filesys_indom; + if (strcmp(type, "tmpfs") == 0) { + indom = tmpfs_indom; + device = path; + } + else if (strncmp(device, "/dev", 4) != 0) + continue; + if (realpath(device, realdevice) != NULL) + device = realdevice; + + sts = pmdaCacheLookupName(indom, device, NULL, (void **)&fs); + if (sts == PMDA_CACHE_ACTIVE) /* repeated line in /proc/mounts? */ + continue; + if (sts == PMDA_CACHE_INACTIVE) { /* re-activate an old mount */ + pmdaCacheStore(indom, PMDA_CACHE_ADD, device, fs); + if (strcmp(path, fs->path) != 0) { /* old device, new path */ + free(fs->path); + fs->path = strdup(path); + } + if (strcmp(options, fs->options) != 0) { /* old device, new opts */ + free(fs->options); + fs->options = strdup(options); + } + } + else { /* new mount */ + if ((fs = malloc(sizeof(filesys_t))) == NULL) + continue; + fs->device = strdup(device); + fs->path = strdup(path); + fs->options = strdup(options); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_filesys: add \"%s\" \"%s\"\n", + fs->path, device); + } +#endif + pmdaCacheStore(indom, PMDA_CACHE_ADD, device, fs); + } + fs->flags = 0; + } + + /* + * success + * Note: we do not call statfs() here since only some instances + * may be requested (rather, we do it in linux_fetch, see pmda.c). + */ + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/filesys.h b/src/pmdas/linux/filesys.h new file mode 100644 index 0000000..d90bdf7 --- /dev/null +++ b/src/pmdas/linux/filesys.h @@ -0,0 +1,32 @@ +/* + * Linux Filesystem Cluster + * + * Copyright (c) 2000,2004,2007 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +/* Values for flags in filesys_t */ +#define FSF_FETCHED (1U << 0) + +typedef struct filesys { + int id; + unsigned int flags; + char *device; + char *path; + char *options; + struct statfs stats; +} filesys_t; + +extern int refresh_filesys(pmInDom, pmInDom); +extern char *scan_filesys_options(const char *, const char *); diff --git a/src/pmdas/linux/getinfo.c b/src/pmdas/linux/getinfo.c new file mode 100644 index 0000000..d5f7f29 --- /dev/null +++ b/src/pmdas/linux/getinfo.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" + +char * +get_distro_info(void) +{ + /* + * Heuristic guesswork ... add code here as we learn + * more about how to identify each Linux distribution. + */ + static char *distro_name; + struct stat sbuf; + int r, sts, fd = -1, len = 0; + char path[MAXPATHLEN]; + char prefix[16]; + enum { /* rfiles array offsets */ + DEB_VERSION = 0, + LSB_RELEASE = 6, + }; + char *rfiles[] = { "debian_version", "oracle-release", "fedora-release", + "redhat-release", "slackware-version", "SuSE-release", "lsb-release", + /* insert any new distribution release variants here */ + NULL + }; + + if (distro_name) + return distro_name; + + for (r = 0; rfiles[r] != NULL; r++) { + snprintf(path, sizeof(path), "%s/etc/%s", linux_statspath, rfiles[r]); + if (stat(path, &sbuf) == 0 && S_ISREG(sbuf.st_mode)) { + fd = open(path, O_RDONLY); + break; + } + } + if (fd != -1) { + if (r == DEB_VERSION) { /* Debian, needs a prefix */ + strncpy(prefix, "Debian ", sizeof(prefix)); + len = 7; + } + /* + * at this point, assume sbuf is good and file contains + * the string we want, probably with a \n terminator + */ + distro_name = (char *)malloc(len + (int)sbuf.st_size + 1); + if (distro_name != NULL) { + if (len) + strncpy(distro_name, prefix, len); + sts = read(fd, distro_name + len, (int)sbuf.st_size); + if (sts <= 0) { + free(distro_name); + distro_name = NULL; + } else { + char *nl; + + if (r == LSB_RELEASE) { /* may be Ubuntu */ + if (!strncmp(distro_name, "DISTRIB_ID = ", 13)) + distro_name += 13; /* ick */ + if (!strncmp(distro_name, "DISTRIB_ID=", 11)) + distro_name += 11; /* more ick */ + } + distro_name[sts + len] = '\0'; + if ((nl = strchr(distro_name, '\n')) != NULL) + *nl = '\0'; + } + } + close(fd); + } + if (distro_name == NULL) + distro_name = "?"; + return distro_name; +} diff --git a/src/pmdas/linux/getinfo.h b/src/pmdas/linux/getinfo.h new file mode 100644 index 0000000..0bf170d --- /dev/null +++ b/src/pmdas/linux/getinfo.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2010 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +extern char *get_distro_info(void); + diff --git a/src/pmdas/linux/help b/src/pmdas/linux/help new file mode 100644 index 0000000..63166cf --- /dev/null +++ b/src/pmdas/linux/help @@ -0,0 +1,1122 @@ +# +# Copyright (c) 2000,2004-2008 Silicon Graphics, Inc. All Rights Reserved. +# Portions Copyright (c) International Business Machines Corp., 2002 +# Portions Copyright (c) 2007-2009 Aconex. All Rights Reserved. +# Portions Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Linux PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# +@ kernel.uname.release release level of the running kernel +Release level of the running kernel as reported via the release[] +value returned from uname(2) or uname -r. + +See also pmda.uname. + +@ kernel.uname.version version level (build number) and build date of the running kernel +Version level of the running kernel as reported by the version[] +value returned from uname(2) or uname -v. Usually a build number +followed by a build date. + +See also pmda.uname. + +@ kernel.uname.sysname name of the implementation of the operating system +Name of the implementation of the running operating system as reported +by the sysname[] value returned from uname(2) or uname -s. Usually +"Linux". + +See also pmda.uname. + +@ kernel.uname.machine name of the hardware type the system is running on +Name of the hardware type the system is running on as reported by the machine[] +value returned from uname(2) or uname -m, e.g. "i686". + +See also pmda.uname. + +@ kernel.uname.nodename host name of this node on the network +Name of this node on the network as reported by the nodename[] +value returned from uname(2) or uname -n. Usually a synonym for +the host name. + +See also pmda.uname. + +@ kernel.uname.distro Linux distribution name +The Linux distribution name, as determined by a number of heuristics. +For example: ++ on Fedora, the contents of /etc/fedora-release ++ on RedHat, the contents of /etc/redhat-release + +@ kernel.percpu.cpu.user percpu user CPU time metric from /proc/stat, including guest CPU time +@ kernel.percpu.cpu.vuser percpu user CPU time metric from /proc/stat, excluding guest CPU time +@ kernel.percpu.cpu.nice percpu nice user CPU time metric from /proc/stat +@ kernel.percpu.cpu.sys percpu sys CPU time metric from /proc/stat +@ kernel.percpu.cpu.idle percpu idle CPU time metric from /proc/stat +@ kernel.percpu.cpu.wait.total percpu wait CPU time +Per-CPU I/O wait CPU time - time spent with outstanding I/O requests. + +@ kernel.percpu.cpu.intr percpu interrupt CPU time +Total time spent processing interrupts on each CPU (this includes +both soft and hard interrupt processing time). + +@ kernel.percpu.cpu.irq.soft percpu soft interrupt CPU time +Per-CPU soft interrupt CPU time (deferred interrupt handling code, +not run in the initial interrupt handler). + +@ kernel.percpu.cpu.irq.hard percpu hard interrupt CPU time +Per-CPU hard interrupt CPU time ("hard" interrupt handling code +is the code run directly on receipt of the initial hardware +interrupt, and does not include "soft" interrupt handling code +which is deferred until later). + +@ kernel.percpu.cpu.steal percpu CPU steal time +Per-CPU time when the CPU had a runnable process, but the hypervisor +(virtualisation layer) chose to run something else instead. + +@ kernel.percpu.cpu.guest percpu guest CPU time +Per-CPU time spent running (virtual) guest operating systems. + +@ kernel.all.interrupts.errors interrupt error count from /proc/interrupts +This is a global counter (normally converted to a count/second) +for any and all errors that occur while handling interrupts. + +@ disk.dev.read per-disk read operations +Cumulative number of disk read operations since system boot time (subject +to counter wrap). + +@ disk.dev.write per-disk write operations +Cumulative number of disk write operations since system boot time (subject +to counter wrap). + +@ disk.dev.total per-disk total (read+write) operations +Cumulative number of disk read and write operations since system boot +time (subject to counter wrap). + +@ disk.dev.blkread per-disk block read operations +Cumulative number of disk block read operations since system boot time +(subject to counter wrap). + +@ disk.dev.blkwrite per-disk block write operations +Cumulative number of disk block write operations since system boot time +(subject to counter wrap). + +@ disk.dev.blktotal per-disk total (read+write) block operations +Cumulative number of disk block read and write operations since system +boot time (subject to counter wrap). + +@ disk.dev.read_bytes per-disk count of bytes read +@ disk.dev.write_bytes per-disk count of bytes written +@ disk.dev.total_bytes per-disk count of total bytes read and written + +@ disk.dev.scheduler per-disk I/O scheduler +The name of the I/O scheduler in use for each device. The scheduler +is part of the block layer in the kernel, and attempts to optimise the +I/O submission patterns using various techniques (typically, sorting +and merging adjacent requests into larger ones to reduce seek activity, +but certainly not limited to that). + +@ disk.dev.avactive per-disk count of active time +When converted to a rate, this metric represents the average utilization of +the disk during the sampling interval. A value of 0.5 (or 50%) means the +disk was active (i.e. busy) half the time. + +@ disk.dev.read_rawactive per-disk raw count of active read time +When converted to a rate, this metric represents the raw utilization of +the disk during the sampling interval as a result of reads. Accounting for +this metric is only done on I/O completion and can thus result in more than a +second's worth of IO being accounted for within any one second, leading to +>100% utilisation. It is suitable mainly for use in calculations with other +metrics, e.g. mirroring the results from existing performance tools: + + iostat.dev.r_await = delta(disk.dev.read_rawactive) / delta(disk.dev.read) + +@ disk.dev.write_rawactive per-disk raw count of active write time +When converted to a rate, this metric represents the raw utilization of +the disk during the sampling interval as a result of writes. Accounting for +this metric is only done on I/O completion and can thus result in more than a +second's worth of IO being accounted for within any one second, leading to +>100% utilisation. It is suitable mainly for use in calculations with other +metrics, e.g. mirroring the results from existing performance tools: + + iostat.dev.w_await = delta(disk.dev.write_rawactive) / delta(disk.dev.write) + +@ disk.dev.aveq per-disk time averaged count of request queue length +When converted to a rate, this metric represents the time averaged disk +request queue length during the sampling interval. A value of 2.5 (or 250%) +represents a time averaged queue length of 2.5 requests during the sampling +interval. + +@ disk.dev.read_merge per-disk count of merged read requests +Count of read requests that were merged with an already queued read request. + +@ disk.dev.write_merge per-disk count of merged write requests +Count of write requests that were merged with an already queued write request. + +@ disk.dm.read per-device-mapper device read operations + +@ disk.dm.write per-device-mapper device write operations + +@ disk.dm.total per-device-mapper device total (read+write) operations + +@ disk.dm.blkread per-device-mapper device block read operations + +@ disk.dm.blkwrite per-device-mapper device block write operations + +@ disk.dm.blktotal per-device-mapper device total (read+write) block operations + +@ disk.dm.read_bytes per-device-mapper device count of bytes read + +@ disk.dm.write_bytes per-device-mapper device count of bytes written + +@ disk.dm.total_bytes per-device-mapper device count of total bytes read and written + +@ disk.dm.read_merge per-device-mapper device count of merged read requests + +@ disk.dm.write_merge per-device-mapper device count of merged write requests + +@ disk.dm.avactive per-device-mapper device count of active time + +@ disk.dm.aveq per-device-mapper device time averaged count of request queue length + +@ disk.dm.read_rawactive per-device-mapper raw count of active read time +When converted to a rate, this metric represents the raw utilization of +the device during the sampling interval as a result of reads. Accounting for +this metric is only done on I/O completion and can thus result in more than a +second's worth of IO being accounted for within any one second, leading to +>100% utilisation. It is suitable mainly for use in calculations with other +metrics, e.g. mirroring the results from existing performance tools: + + iostat.dm.r_await = delta(disk.dm.read_rawactive) / delta(disk.dm.read) + +@ disk.dm.write_rawactive per-device-mapper raw count of active write time +When converted to a rate, this metric represents the raw utilization of +the device during the sampling interval as a result of writes. Accounting for +this metric is only done on I/O completion and can thus result in more than a +second's worth of IO being accounted for within any one second, leading to +>100% utilisation. It is suitable mainly for use in calculations with other +metrics, e.g. mirroring the results from existing performance tools: + + iostat.dm.w_await = delta(disk.dm.write_rawactive) / delta(disk.dm.write) + +@ hinv.map.dmname per-device-mapper device persistent name mapping to dm-[0-9]* + +@ disk.all.read_merge total count of merged read requests, summed for all disks +Total count of read requests that were merged with an already queued read request. + +@ disk.all.write_merge total count of merged write requests, summed for all disks +Total count of write requests that were merged with an already queued write request. + +@ disk.all.avactive total count of active time, summed for all disks +When converted to a rate, this metric represents the average utilization of +all disks during the sampling interval. A value of 0.25 (or 25%) means that +on average every disk was active (i.e. busy) one quarter of the time. + +@ disk.all.read_rawactive raw count of active read time, summed for all disks +When converted to a rate, this metric represents the raw utilization of all +disks during the sampling interval due to read requests. The accounting for +this metric is only done on I/O completion and can thus result in more than a +second's worth of IO being accounted for within any one second, leading to +>100% utilisation. It is suitable mainly for use in calculations with other +metrics, e.g. mirroring the results from existing performance tools: + + iostat.all.r_await = delta(disk.all.read_rawactive) / delta(disk.all.read) + +@ disk.all.write_rawactive raw count of active write time, summed for all disks +When converted to a rate, this metric represents the raw utilization of all +disks during the sampling interval due to write requests. The accounting for +this metric is only done on I/O completion and can thus result in more than a +second's worth of IO being accounted for within any one second, leading to +>100% utilisation. It is suitable mainly for use in calculations with other +metrics, e.g. mirroring the result from existing performance tools: + + iostat.all.w_await = delta(disk.all.write_rawactive) / delta(disk.all.write) + +@ disk.all.aveq total time averaged count of request queue length, summed for all disks +When converted to a rate, this metric represents the average across all disks +of the time averaged request queue length during the sampling interval. A +value of 1.5 (or 150%) suggests that (on average) each all disk experienced a +time averaged queue length of 1.5 requests during the sampling interval. + +@ disk.all.read total read operations, summed for all disks +Cumulative number of disk read operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.write total write operations, summed for all disks +Cumulative number of disk read operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.total total (read+write) operations, summed for all disks +Cumulative number of disk read and write operations since system boot +time (subject to counter wrap), summed over all disk devices. + +@ disk.all.blkread block read operations, summed for all disks +Cumulative number of disk block read operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.blkwrite block write operations, summed for all disks +Cumulative number of disk block write operations since system boot time +(subject to counter wrap), summed over all disk devices. + +@ disk.all.blktotal total (read+write) block operations, summed for all disks +Cumulative number of disk block read and write operations since system +boot time (subject to counter wrap), summed over all disk devices. + +@ disk.all.read_bytes count of bytes read for all disk devices +@ disk.all.write_bytes count of bytes written for all disk devices +@ disk.all.total_bytes total count of bytes read and written for all disk devices + +@ disk.partitions.read read operations metric for storage partitions +Cumulative number of disk read operations since system boot time +(subject to counter wrap) for individual disk partitions or logical +volumes. + +@ disk.partitions.write write operations metric for storage partitions +Cumulative number of disk write operations since system boot time +(subject to counter wrap) for individual disk partitions or logical +volumes. + +@ disk.partitions.total total (read+write) I/O operations metric for storage partitions +Cumulative number of disk read and write operations since system boot +time (subject to counter wrap) for individual disk partitions or +logical volumes. + +@ disk.partitions.blkread block read operations metric for storage partitions +Cumulative number of disk block read operations since system boot time +(subject to counter wrap) for individual disk partitions or logical +volumes. + +@ disk.partitions.blkwrite block write operations metric for storage partitions +Cumulative number of disk block write operations since system boot time +(subject to counter wrap) for individual disk partitions or logical +volumes. + +@ disk.partitions.blktotal total (read+write) block operations metric for storage partitions +Cumulative number of disk block read and write operations since system +boot time (subject to counter wrap) for individual disk partitions or +logical volumes. + +@ disk.partitions.read_bytes number of bytes read for storage partitions +Cumulative number of bytes read since system boot time (subject to +counter wrap) for individual disk partitions or logical volumes. + +@ disk.partitions.write_bytes number of bytes written for storage partitions +Cumulative number of bytes written since system boot time (subject to +counter wrap) for individual disk partitions or logical volumes. + +@ disk.partitions.total_bytes total number of bytes read and written for storage partitions +Cumulative number of bytes read and written since system boot time +(subject to counter wrap) for individual disk partitions or logical +volumes. + +@ swap.pagesin pages read from swap devices due to demand for physical memory +@ swap.pagesout pages written to swap devices due to demand for physical memory +@ swap.in number of swap in operations +@ swap.out number of swap out operations +@ kernel.all.pswitch context switches metric from /proc/stat +@ kernel.all.sysfork fork rate metric from /proc/stat +@ kernel.all.intr intrrupt rate metric from /proc/stat +The value is the first value from the intr field in /proc/stat, +which is a counter of the total number of interrupts processed. +The value is normally converted to a rate (count/second). +This counter usually increases by at least HZ/second, +i.e. the clock interrupt rate, wehich is usually 100/second. + +See also kernel.percpu.interrupts to get a breakdown +of interrupt rates by interrupt type and which CPU +processed each one. + +@ mem.physmem total system memory metric reported by /proc/meminfo +The value of this metric corresponds to the "MemTotal" field +reported by /proc/meminfo. Note that this does not necessarily +correspond to actual installed physical memory - there may +be areas of the physical address space mapped as ROM in +various peripheral devices and the bios may be mirroring +certain ROMs in RAM. +@ mem.freemem free system memory metric from /proc/meminfo +@ mem.util.used used memory metric from /proc/meminfo +Used memory is the difference between mem.physmem and mem.freemem. +@ mem.util.free free memory metric from /proc/meminfo +Alias for mem.freemem. +@ mem.util.available available memory from /proc/meminfo +The amount of memory that is available for a new workload, +without pushing the system into swap. Estimated from MemFree, +Active(file), Inactive(file), and SReclaimable, as well as the "low" +watermarks from /proc/zoneinfo. + +@ mem.util.shared shared memory metric from /proc/meminfo +Shared memory metric. Currently always zero on Linux 2.4 kernels +and has been removed from 2.6 kernels. +@ mem.util.bufmem I/O buffers metric from /proc/meminfo +Memory allocated for buffer_heads. +@ mem.util.cached page cache metric from /proc/meminfo +Memory used by the page cache, including buffered file data. +This is in-memory cache for files read from the disk (the pagecache) +but doesn't include SwapCached. +@ mem.util.other unaccounted memory +Memory that is not free (i.e. has been referenced) and is not cached. +mem.physmem - mem.util.free - mem.util.cached - mem.util.buffers +@ mem.util.active Kbytes on the active page list (recently referenced pages) +Memory that has been used more recently and usually not reclaimed unless +absolutely necessary. +@ mem.util.inactive Kbytes on the inactive page list (candidates for discarding) +Memory which has been less recently used. It is more eligible to be +reclaimed for other purposes +@ mem.util.swapCached Kbytes in swap cache, from /proc/meminfo +Memory that once was swapped out, is swapped back in but still also +is in the swapfile (if memory is needed it doesn't need to be swapped +out AGAIN because it is already in the swapfile. This saves I/O) +@ mem.util.highTotal Kbytes in high memory, from /proc/meminfo +This is apparently an i386 specific metric, and seems to be always zero +on ia64 architecture (and possibly others). On i386 arch (at least), +highmem is all memory above ~860MB of physical memory. Highmem areas +are for use by userspace programs, or for the pagecache. The kernel +must use tricks to access this memory, making it slower to access +than lowmem. +@ mem.util.highFree Kbytes free high memory, from /proc/meminfo +See mem.util.highTotal. Not used on ia64 arch (and possibly others). +@ mem.util.lowTotal Kbytes in low memory total, from /proc/meminfo +Lowmem is memory which can be used for everything that highmem can be +used for, but it is also availble for the kernel's use for its own +data structures. Among many other things, it is where everything +from the Slab is allocated. Bad things happen when you're out of lowmem. +(this may only be true on i386 architectures). +@ mem.util.lowFree Kbytes free low memory, from /proc/meminfo +See mem.util.lowTotal +@ mem.util.swapTotal Kbytes swap, from /proc/meminfo +total amount of swap space available +@ mem.util.swapFree Kbytes free swap, from /proc/meminfo +Memory which has been evicted from RAM, and is temporarily on the disk +@ mem.util.dirty Kbytes in dirty pages, from /proc/meminfo +Memory which is waiting to get written back to the disk +@ mem.util.writeback Kbytes in writeback pages, from /proc/meminfo +Memory which is actively being written back to the disk +@ mem.util.mapped Kbytes in mapped pages, from /proc/meminfo +files which have been mmaped, such as libraries +@ mem.util.slab Kbytes in slab memory, from /proc/meminfo +in-kernel data structures cache +@ mem.util.commitLimit Kbytes limit for address space commit, from /proc/meminfo +The static total, in Kbytes, available for commitment to address +spaces. Thus, mem.util.committed_AS may range up to this total. Normally +the kernel overcommits memory, so this value may exceed mem.physmem +@ mem.util.committed_AS Kbytes committed to address spaces, from /proc/meminfo +An estimate of how much RAM you would need to make a 99.99% guarantee +that there never is OOM (out of memory) for this workload. Normally +the kernel will overcommit memory. That means, say you do a 1GB malloc, +nothing happens, really. Only when you start USING that malloc memory +you will get real memory on demand, and just as much as you use. +@ mem.util.pageTables Kbytes in kernel page tables, from /proc/meminfo +@ mem.util.reverseMaps Kbytes in reverse mapped pages, from /proc/meminfo +@ mem.util.cache_clean Kbytes cached and not dirty or writeback, derived from /proc/meminfo +@ mem.util.anonpages Kbytes in user pages not backed by files, from /proc/meminfo +@ mem.util.bounce Kbytes in bounce buffers, from /proc/meminfo +@ mem.util.NFS_Unstable Kbytes in NFS unstable memory, from /proc/meminfo +@ mem.util.slabReclaimable Kbytes in reclaimable slab pages, from /proc/meminfo +@ mem.util.slabUnreclaimable Kbytes in unreclaimable slab pages, from /proc/meminfo +@ mem.util.active_anon anonymous Active list LRU memory +@ mem.util.inactive_anon anonymous Inactive list LRU memory +@ mem.util.active_file file-backed Active list LRU memory +@ mem.util.inactive_file file-backed Inactive list LRU memory +@ mem.util.unevictable kbytes of memory that is unevictable +@ mem.util.mlocked kbytes of memory that is pinned via mlock() +@ mem.util.shmem kbytes of shmem +@ mem.util.kernelStack kbytes of memory used for kernel stacks +@ mem.util.hugepagesTotal a count of total hugepages +@ mem.util.hugepagesFree a count of free hugepages +@ mem.util.hugepagesSurp a count of surplus hugepages +@ mem.util.directMap4k amount of memory that is directly mapped in 4kB pages +@ mem.util.directMap2M amount of memory that is directly mapped in 2MB pages +@ mem.util.directMap1G amount of memory that is directly mapped in 1GB pages +@ mem.util.vmallocTotal amount of kernel memory allocated via vmalloc +@ mem.util.vmallocUsed amount of used vmalloc memory +@ mem.util.vmallocChunk amount of vmalloc chunk memory +@ mem.util.mmap_copy amount of mmap_copy space (non-MMU kernels only) +@ mem.util.quicklists amount of memory in the per-CPU quicklists +@ mem.util.corrupthardware amount of memory in hardware corrupted pages +@ mem.util.anonhugepages amount of memory in anonymous huge pages + +User memory (Kbytes) in pages not backed by files, e.g. from malloc() +@ mem.numa.util.total per-node total memory +@ mem.numa.util.free per-node free memory +@ mem.numa.util.used per-node used memory +@ mem.numa.util.active per-node Active list LRU memory +@ mem.numa.util.inactive per-node Inactive list LRU memory +@ mem.numa.util.active_anon per-node anonymous Active list LRU memory +@ mem.numa.util.inactive_anon per-node anonymous Inactive list LRU memory +@ mem.numa.util.active_file per-node file-backed Active list LRU memory +@ mem.numa.util.inactive_file per-node file-backed Inactive list LRU memory +@ mem.numa.util.highTotal per-node highmem total +@ mem.numa.util.highFree per-node highmem free +@ mem.numa.util.lowTotal per-node lowmem total +@ mem.numa.util.lowFree per-node lowmem free +@ mem.numa.util.unevictable per-node Unevictable memory +@ mem.numa.util.mlocked per-node count of Mlocked memory +@ mem.numa.util.dirty per-node dirty memory +@ mem.numa.util.writeback per-node count of memory locked for writeback to stable storage +@ mem.numa.util.filePages per-node count of memory backed by files +@ mem.numa.util.mapped per-node mapped memory +@ mem.numa.util.anonpages per-node anonymous memory +@ mem.numa.util.shmem per-node amount of shared memory +@ mem.numa.util.kernelStack per-node memory used as kernel stacks +@ mem.numa.util.pageTables per-node memory used for pagetables +@ mem.numa.util.NFS_Unstable per-node memory holding NFS data that needs writeback +@ mem.numa.util.bounce per-node memory used for bounce buffers +@ mem.numa.util.writebackTmp per-node temporary memory used for writeback +@ mem.numa.util.slab per-node memory used for slab objects +@ mem.numa.util.slabReclaimable per-node memory used for slab objects that can be reclaimed +@ mem.numa.util.slabUnreclaimable per-node memory used for slab objects that is unreclaimable +@ mem.numa.util.hugepagesTotal per-node total count of hugepages +@ mem.numa.util.hugepagesFree per-node count of free hugepages +@ mem.numa.util.hugepagesSurp per-node count of surplus hugepages +@ mem.numa.alloc.hit per-node count of times a task wanted alloc on local node and succeeded +@ mem.numa.alloc.miss per-node count of times a task wanted alloc on local node but got another node +@ mem.numa.alloc.foreign count of times a task on another node alloced on that node, but got this node +@ mem.numa.alloc.interleave_hit count of times interleaving wanted to allocate on this node and succeeded +@ mem.numa.alloc.local_node count of times a process ran on this node and got memory on this node +@ mem.numa.alloc.other_node count of times a process ran on this node and got memory from another node +@ mem.vmstat.nr_dirty number of pages in dirty state +Instantaneous number of pages in dirty state, from /proc/vmstat +@ mem.vmstat.nr_dirtied count of pages dirtied +Count of pages entering dirty state, from /proc/vmstat +@ mem.vmstat.nr_writeback number of pages in writeback state +Instantaneous number of pages in writeback state, from /proc/vmstat +@ mem.vmstat.nr_unstable number of pages in unstable state +Instantaneous number of pages in unstable state, from /proc/vmstat +@ mem.vmstat.nr_page_table_pages number of page table pages +Instantaneous number of page table pages, from /proc/vmstat +@ mem.vmstat.nr_mapped number of mapped pagecache pages +Instantaneous number of mapped pagecache pages, from /proc/vmstat +See also mem.vmstat.nr_anon for anonymous mapped pages. +@ mem.vmstat.nr_slab number of slab pages +Instantaneous number of slab pages, from /proc/vmstat +This counter was retired in 2.6.18 kernels, and is now the sum of +mem.vmstat.nr_slab_reclaimable and mem.vmstat.nr_slab_unreclaimable. +@ mem.vmstat.nr_written count of pages written out +Count of pages written out, from /proc/vmstat +@ mem.vmstat.numa_foreign count of foreign NUMA zone allocations +@ mem.vmstat.numa_hit count of successful allocations from preferred NUMA zone +@ mem.vmstat.numa_interleave count of interleaved NUMA allocations +@ mem.vmstat.numa_local count of successful allocations from local NUMA zone +@ mem.vmstat.numa_miss count of unsuccessful allocations from preferred NUMA zona +@ mem.vmstat.numa_other count of unsuccessful allocations from local NUMA zone +@ mem.vmstat.pgpgin page in operations +Count of page in operations since boot, from /proc/vmstat +@ mem.vmstat.pgpgout page out operations +Count of page out operations since boot, from /proc/vmstat +@ mem.vmstat.pswpin pages swapped in +Count of pages swapped in since boot, from /proc/vmstat +@ mem.vmstat.pswpout pages swapped out +Count of pages swapped out since boot, from /proc/vmstat +@ mem.vmstat.pgalloc_high high mem page allocations +Count of high mem page allocations since boot, from /proc/vmstat +@ mem.vmstat.pgalloc_normal normal mem page allocations +Count of normal mem page allocations since boot, from /proc/vmstat +@ mem.vmstat.pgalloc_dma dma mem page allocations +Count of dma mem page allocations since boot, from /proc/vmstat +@ mem.vmstat.pgalloc_dma32 dma32 mem page allocations +Count of dma32 mem page allocations since boot, from /proc/vmstat +@ mem.vmstat.pgalloc_movable movable mem page allocations +Count of movable mem page allocations since boot, from /proc/vmstat +@ mem.vmstat.pgfree page free operations +Count of page free operations since boot, from /proc/vmstat +@ mem.vmstat.pgactivate pages moved from inactive to active +Count of pages moved from inactive to active since boot, from /proc/vmstat +@ mem.vmstat.pgdeactivate pages moved from active to inactive +Count of pages moved from active to inactive since boot, from /proc/vmstat +@ mem.vmstat.pgfault page major and minor fault operations +Count of page major and minor fault operations since boot, from /proc/vmstat +@ mem.vmstat.pgmajfault major page fault operations +Count of major page fault operations since boot, from /proc/vmstat +@ mem.vmstat.pgrefill_high high mem pages inspected in refill_inactive_zone +Count of high mem pages inspected in refill_inactive_zone since boot, +from /proc/vmstat +@ mem.vmstat.pgrefill_normal normal mem pages inspected in refill_inactive_zone +Count of normal mem pages inspected in refill_inactive_zone since boot, +from /proc/vmstat +@ mem.vmstat.pgrefill_dma dma mem pages inspected in refill_inactive_zone +Count of dma mem pages inspected in refill_inactive_zone since boot, +from /proc/vmstat +@ mem.vmstat.pgrefill_dma32 dma32 mem pages inspected in refill_inactive_zone +Count of dma32 mem pages inspected in refill_inactive_zone since boot, +from /proc/vmstat +@ mem.vmstat.pgrefill_movable movable mem pages inspected in refill_inactive_zone +Count of movable mem pages inspected in refill_inactive_zone since boot, +from /proc/vmstat +@ mem.vmstat.pgsteal_high high mem pages reclaimed +Count of high mem pages reclaimed since boot, from /proc/vmstat +@ mem.vmstat.pgsteal_normal normal mem pages reclaimed +Count of normal mem pages reclaimed since boot, from /proc/vmstat +@ mem.vmstat.pgsteal_dma dma mem pages reclaimed +Count of dma mem pages reclaimed since boot, from /proc/vmstat +@ mem.vmstat.pgsteal_dma32 dma32 mem pages reclaimed +Count of dma32 mem pages reclaimed since boot, from /proc/vmstat +@ mem.vmstat.pgsteal_movable movable mem pages reclaimed +Count of movable mem pages reclaimed since boot, from /proc/vmstat +@ mem.vmstat.pgscan_kswapd_high high mem pages scanned by kswapd +Count of high mem pages scanned by kswapd since boot, from /proc/vmstat +@ mem.vmstat.pgscan_kswapd_normal normal mem pages scanned by kswapd +Count of normal mem pages scanned by kswapd since boot, from /proc/vmstat +@ mem.vmstat.pgscan_kswapd_dma dma mem pages scanned by kswapd +Count of dma mem pages scanned by kswapd since boot, from /proc/vmstat +@ mem.vmstat.pgscan_kswapd_dma32 dma32 mem pages scanned by kswapd +Count of dma32 mem pages scanned by kswapd since boot, from /proc/vmstat +@ mem.vmstat.pgscan_kswapd_movable movable mem pages scanned by kswapd +Count of movable mem pages scanned by kswapd since boot, from /proc/vmstat +@ mem.vmstat.pgscan_direct_high high mem pages scanned +Count of high mem pages scanned since boot, from /proc/vmstat +@ mem.vmstat.pgscan_direct_normal normal mem pages scanned +Count of normal mem pages scanned since boot, from /proc/vmstat +@ mem.vmstat.pgscan_direct_dma dma mem pages scanned +Count of dma mem pages scanned since boot, from /proc/vmstat +@ mem.vmstat.pgscan_direct_dma32 dma32 mem pages scanned +Count of dma32 mem pages scanned since boot, from /proc/vmstat +@ mem.vmstat.pgscan_direct_movable +Count of movable mem pages scanned since boot, from /proc/vmstat +@ mem.vmstat.pginodesteal pages reclaimed via inode freeing +Count of pages reclaimed via inode freeing since boot, from /proc/vmstat +@ mem.vmstat.slabs_scanned slab pages scanned +Count of slab pages scanned since boot, from /proc/vmstat +@ mem.vmstat.kswapd_steal pages reclaimed by kswapd +Count of pages reclaimed by kswapd since boot, from /proc/vmstat +@ mem.vmstat.kswapd_low_wmark_hit_quickly count of times low watermark reached quickly +Count of times kswapd reached low watermark quickly, from /proc/vmstat +@ mem.vmstat.kswapd_high_wmark_hit_quickly count of times high watermark reached quickly +Count of times kswapd reached high watermark quickly, from /proc/vmstat +@ mem.vmstat.kswapd_skip_congestion_wait count of times kswapd skipped waiting on device congestion +Count of times kswapd skipped waiting due to device congestion as a +result of being under the low watermark, from /proc/vmstat +@ mem.vmstat.kswapd_inodesteal pages reclaimed via kswapd inode freeing +Count of pages reclaimed via kswapd inode freeing since boot, from +/proc/vmstat +@ mem.vmstat.pageoutrun kswapd calls to page reclaim +Count of kswapd calls to page reclaim since boot, from /proc/vmstat +@ mem.vmstat.allocstall direct reclaim calls +Count of direct reclaim calls since boot, from /proc/vmstat +@ mem.vmstat.pgrotated pages rotated to tail of the LRU +Count of pages rotated to tail of the LRU since boot, from /proc/vmstat +@mem.vmstat.nr_anon_pages number of anonymous mapped pagecache pages +Instantaneous number of anonymous mapped pagecache pages, from /proc/vmstat +See also mem.vmstat.mapped for other mapped pages. +@mem.vmstat.nr_anon_transparent_hugepages number of anonymous transparent huge pages +Instantaneous number of anonymous transparent huge pages, from /proc/vmstat +@mem.vmstat.nr_bounce number of bounce buffer pages +Instantaneous number of bounce buffer pages, from /proc/vmstat +@mem.vmstat.nr_slab_reclaimable reclaimable slab pages +Instantaneous number of reclaimable slab pages, from /proc/vmstat. +@mem.vmstat.nr_slab_unreclaimable unreclaimable slab pages +Instantaneous number of unreclaimable slab pages, from /proc/vmstat. +@mem.vmstat.nr_vmscan_write pages written by VM scanner from LRU +Count of pages written from the LRU by the VM scanner, from /proc/vmstat. +The VM is supposed to minimise the number of pages which get written +from the LRU (for IO scheduling efficiency, and for high reclaim-success +rates). +@ mem.vmstat.htlb_buddy_alloc_fail huge TLB page buddy allocation failures +Count of huge TLB page buddy allocation failures, from /proc/vmstat +@ mem.vmstat.htlb_buddy_alloc_success huge TLB page buddy allocation successes +Count of huge TLB page buddy allocation successes, from /proc/vmstat +@ mem.vmstat.nr_active_anon number of active anonymous memory pages +@ mem.vmstat.nr_active_file number of active file memory memory pages +@ mem.vmstat.nr_free_pages number of free pages +@ mem.vmstat.nr_inactive_anon number of inactive anonymous memory pages +@ mem.vmstat.nr_inactive_file number of inactive file memory pages +@ mem.vmstat.nr_isolated_anon number of isolated anonymous memory pages +@ mem.vmstat.nr_isolated_file number of isolated file memory pages +@ mem.vmstat.nr_kernel_stack number of pages of kernel stack +@ mem.vmstat.nr_mlock number of pages under mlock +@ mem.vmstat.nr_shmem number of shared memory pages +@ mem.vmstat.nr_unevictable number of unevictable pages +@ mem.vmstat.nr_writeback_temp number of temporary writeback pages +@ mem.vmstat.compact_blocks_moved count of compact blocks moved +@ mem.vmstat.compact_fail count of unsuccessful compactions for high order allocations +@ mem.vmstat.compact_pagemigrate_failed count of pages unsuccessfully compacted +@ mem.vmstat.compact_pages_moved count of pages successfully moved for compaction +@ mem.vmstat.compact_stall count of failures to even start compacting +@ mem.vmstat.compact_success count of successful compactions for high order allocations +@ mem.vmstat.thp_fault_alloc transparent huge page fault allocations +@ mem.vmstat.thp_fault_fallback transparent huge page fault fallbacks +@ mem.vmstat.thp_collapse_alloc transparent huge page collapse allocations +@ mem.vmstat.thp_collapse_alloc_failed transparent huge page collapse failures +@ mem.vmstat.thp_split count of transparent huge page splits +@ mem.vmstat.unevictable_pgs_cleared count of unevictable pages cleared +@ mem.vmstat.unevictable_pgs_culled count of unevictable pages culled +@ mem.vmstat.unevictable_pgs_mlocked count of mlocked unevictable pages +@ mem.vmstat.unevictable_pgs_mlockfreed count of unevictable pages mlock freed +@ mem.vmstat.unevictable_pgs_munlocked count of unevictable pages munlocked +@ mem.vmstat.unevictable_pgs_rescued count of unevictable pages rescued +@ mem.vmstat.unevictable_pgs_scanned count of unevictable pages scanned +@ mem.vmstat.unevictable_pgs_stranded count of unevictable pages stranded +@ mem.vmstat.zone_reclaim_failed number of zone reclaim failures + + +@ swap.length total swap available metric from /proc/meminfo +@ swap.used swap used metric from /proc/meminfo +@ swap.free swap free metric from /proc/meminfo +@ kernel.all.load 1, 5 and 15 minute load average +@ kernel.all.cpu.user total user CPU time from /proc/stat for all CPUs, including guest CPU time +@ kernel.all.cpu.vuser total user CPU time from /proc/stat for all CPUs, excluding guest CPU time +@ kernel.all.cpu.intr total interrupt CPU time from /proc/stat for all CPUs +Total time spent processing interrupts on all CPUs. +This value includes both soft and hard interrupt processing time. +@ kernel.all.cpu.wait.total total wait CPU time from /proc/stat for all CPUs +@ kernel.all.cpu.nice total nice user CPU time from /proc/stat for all CPUs +@ kernel.all.cpu.sys total sys CPU time from /proc/stat for all CPUs +@ kernel.all.cpu.idle total idle CPU time from /proc/stat for all CPUs +@ kernel.all.cpu.irq.soft soft interrupt CPU time from /proc/stat for all CPUs +Total soft interrupt CPU time (deferred interrupt handling code, +not run in the initial interrupt handler). +@ kernel.all.cpu.irq.hard hard interrupt CPU time from /proc/stat for all CPUs +Total hard interrupt CPU time ("hard" interrupt handling code +is the code run directly on receipt of the initial hardware +interrupt, and does not include "soft" interrupt handling code +which is deferred until later). +@ kernel.all.cpu.steal total virtualisation CPU steal time for all CPUs +Total CPU time when a CPU had a runnable process, but the hypervisor +(virtualisation layer) chose to run something else instead. +@ kernel.all.cpu.guest total virtual guest CPU time for all CPUs +Total CPU time spent running virtual guest operating systems. +@ kernel.all.nusers number of user sessions on system + +@ hinv.ninterface number of active (up) network interfaces +@ network.interface.in.bytes network recv read bytes from /proc/net/dev per network interface +@ network.interface.in.packets network recv read packets from /proc/net/dev per network interface +@ network.interface.in.errors network recv read errors from /proc/net/dev per network interface +@ network.interface.in.drops network recv read drops from /proc/net/dev per network interface +@ network.interface.in.mcasts network recv compressed from /proc/net/dev per network interface +@ network.interface.in.fifo network recv read fifos from /proc/net/dev per network interface +@ network.interface.in.frame network recv read frames from /proc/net/dev per network interface +@ network.interface.in.compressed network recv compressed from /proc/net/dev per network interface +@ network.interface.out.bytes network send bytes from /proc/net/dev per network interface +@ network.interface.out.packets network send packets from /proc/net/dev per network interface +@ network.interface.out.errors network send errors from /proc/net/dev per network interface +@ network.interface.out.drops network send drops from /proc/net/dev per network interface +@ network.interface.out.fifo network send fifos from /proc/net/dev per network interface +@ network.interface.collisions network send collisions from /proc/net/dev per network interface +@ network.interface.out.carrier network send carrier from /proc/net/dev per network interface +@ network.interface.out.compressed network send compressed from /proc/net/dev per network interface +@ network.interface.total.bytes network total (in+out) bytes from /proc/net/dev per network interface +@ network.interface.total.packets network total (in+out) packets from /proc/net/dev per network interface +@ network.interface.total.errors network total (in+out) errors from /proc/net/dev per network interface +@ network.interface.total.drops network total (in+out) drops from /proc/net/dev per network interface +@ network.interface.total.mcasts network total (in+out) mcasts from /proc/net/dev per network interface +@ network.interface.mtu maximum transmission unit on network interface +@ network.interface.speed interface speed in megabytes per second +The linespeed on the network interface, as reported by the kernel, +scaled from Megabits/second to Megabytes/second. +See also network.interface.baudrate for the bytes/second value. +@ network.interface.baudrate interface speed in bytes per second +The linespeed on the network interface, as reported by the kernel, +scaled up from Megabits/second to bits/second and divided by 8 to convert +to bytes/second. +See also network.interface.speed for the Megabytes/second value. +@ network.interface.duplex value one for half or two for full duplex interface +@ network.interface.up boolean for whether interface is currently up or down +@ network.interface.running boolean for whether interface has resources allocated +@ network.interface.inet_addr string INET interface address (ifconfig style) +@ network.interface.ipv6_addr string IPv6 interface address (ifconfig style) +@ network.interface.ipv6_scope string IPv6 interface scope (ifconfig style) +@ network.interface.hw_addr hardware address (from sysfs) +@ network.sockstat.tcp.inuse instantaneous number of tcp sockets currently in use +@ network.sockstat.tcp.highest highest number of tcp sockets in use at any one time since boot +@ network.sockstat.tcp.util instantaneous tcp socket utilization (100 * inuse/highest) +@ network.sockstat.udp.inuse instantaneous number of udp sockets currently in use +@ network.sockstat.udp.highest highest number of udp sockets in use at any one time since boot +@ network.sockstat.udp.util instantaneous udp socket utilization (100 * inuse/highest) +@ network.sockstat.raw.inuse instantaneous number of raw sockets currently in use +@ network.sockstat.raw.highest highest number of raw sockets in use at any one time since boot +@ network.sockstat.raw.util instantaneous raw socket utilization (100 * inuse/highest) +@ hinv.physmem total system memory metric from /proc/meminfo +@ hinv.pagesize Memory page size +The memory page size of the running kernel in bytes. +@ hinv.ncpu number of CPUs in the system +@ hinv.ndisk number of disks in the system +@ hinv.nfilesys number of (local) file systems currently mounted +@ hinv.nnode number of NUMA nodes in the system +@ hinv.map.scsi list of active SCSI devices +There is one string value for each SCSI device active in the system, +as extracted from /proc/scsi/scsi. The external instance name +for each device is in the format scsiD:C:I:L where +D is controller number, C is channel number, I is device ID +and L is the SCSI LUN number for the device. The values for this +metric are the actual device names (sd[a-z] are SCSI disks, st[0-9] +are SCSI tapes and scd[0-9] are SCSI CD-ROMS. +@ hinv.nlv number of logical volumes +@ hinv.map.lvname mapping of logical volume names for devices +Provides a logical-volume-name to device-name mapping for the device +mapper subsystem. +@ filesys.capacity Total capacity of mounted filesystem (Kbytes) +@ filesys.used Total space used on mounted filesystem (Kbytes) +@ filesys.free Total space free on mounted filesystem (Kbytes) +@ filesys.maxfiles Inodes capacity of mounted filesystem +@ filesys.usedfiles Number of inodes allocated on mounted filesystem +@ filesys.freefiles Number of unallocated inodes on mounted filesystem +@ filesys.mountdir File system mount point +@ filesys.full Percentage of filesystem in use +@ filesys.blocksize Size of each block on mounted filesystem (Bytes) +@ filesys.avail Total space free to non-superusers on mounted filesystem (Kbytes) +@ filesys.readonly Indicates whether a filesystem is mounted readonly +@ tmpfs.capacity Total capacity of mounted tmpfs filesystem (Kbytes) +@ tmpfs.used Total space used on mounted tmpfs filesystem (Kbytes) +@ tmpfs.free Total space free on mounted tmpfs filesystem (Kbytes) +@ tmpfs.maxfiles Inodes capacity of mounted tmpfs filesystem +@ tmpfs.usedfiles Number of inodes allocated on mounted tmpfs filesystem +@ tmpfs.freefiles Number of unallocated inodes on mounted tmpfs filesystem +@ tmpfs.full Percentage of tmpfs filesystem in use +@ swapdev.free physical swap free space +@ swapdev.length physical swap size +@ swapdev.maxswap maximum swap length (same as swapdev.length on Linux) +@ swapdev.vlength virtual swap size (always zero on Linux) +Virtual swap size (always zero on Linux since Linux does not support +virtual swap). + +This metric is retained on Linux for interoperability with PCP monitor +tools running on IRIX. + +@ swapdev.priority swap resource priority +@ nfs.client.calls cumulative total of client NFSv2 requests +@ nfs.client.reqs cumulative total of client NFSv2 requests by request type +@ nfs.server.calls cumulative total of server NFSv2 requests +@ nfs.server.reqs cumulative total of client NFSv2 requests by request type +@ nfs3.client.calls cumulative total of client NFSv3 requests +@ nfs3.client.reqs cumulative total of client NFSv3 requests by request type +@ nfs3.server.calls cumulative total of server NFSv3 requests +@ nfs3.server.reqs cumulative total of client NFSv3 requests by request type +@ nfs4.client.calls cumulative total of client NFSv4 requests +@ nfs4.client.reqs cumulative total for each client NFSv4 request type +@ nfs4.server.calls cumulative total of server NFSv4 operations, plus NULL requests +@ nfs4.server.reqs cumulative total for each server NFSv4 operation, and for NULL requests +@ rpc.client.rpccnt cumulative total of client RPC requests +@ rpc.client.rpcretrans cumulative total of client RPC retransmissions +@ rpc.client.rpcauthrefresh cumulative total of client RPC auth refreshes +@ rpc.client.netcnt cumulative total of client RPC network layer requests +@ rpc.client.netudpcnt cumulative total of client RPC UDP network layer requests +@ rpc.client.nettcpcnt cumulative total of client RPC TCP network layer requests +@ rpc.client.nettcpconn cumulative total of client RPC TCP network layer connection requests +@ rpc.server.rpccnt cumulative total of server RPC requests +@ rpc.server.rpcerr cumulative total of server RPC errors +@ rpc.server.rpcbadfmt cumulative total of server RPC bad format errors +@ rpc.server.rpcbadauth cumulative total of server RPC bad auth errors +@ rpc.server.rpcbadclnt cumulative total of server RPC bad client errors +@ rpc.server.rchits cumulative total of request-reply-cache hits +@ rpc.server.rcmisses cumulative total of request-reply-cache misses +@ rpc.server.rcnocache cumulative total of uncached request-reply-cache requests +@ rpc.server.fh_cached cumulative total of file handle cache requests +@ rpc.server.fh_valid cumulative total of file handle cache validations +@ rpc.server.fh_fixup cumulative total of file handle cache fixup validations +@ rpc.server.fh_lookup cumulative total of file handle cache new lookups +@ rpc.server.fh_stale cumulative total of stale file handle cache errors +@ rpc.server.fh_concurrent cumulative total of concurrent file handle cache requests +@ rpc.server.netcnt cumulative total of server RPC network layer requests +@ rpc.server.netudpcnt cumulative total of server RPC UDP network layer requests +@ rpc.server.nettcpcnt cumulative total of server RPC TCP network layer requests +@ rpc.server.nettcpconn cumulative total of server RPC TCP network layer connection requests +@ rpc.server.fh_anon cumulative total anonymous file dentries returned +@ rpc.server.fh_nocache_dir count of directory file handles not found cached +@ rpc.server.fh_nocache_nondir count of non-directory file handles not found cached +@ rpc.server.io_read cumulative count of bytes returned from read requests +@ rpc.server.io_write cumulative count of bytes passed into write requests +@ rpc.server.th_cnt available nfsd threads +@ rpc.server.th_fullcnt number of times the last free nfsd thread was used + +@ network.ip.forwarding count of ip forwarding +@ network.ip.defaultttl count of ip defaultttl +@ network.ip.inreceives count of ip inreceives +@ network.ip.inhdrerrors count of ip inhdrerrors +@ network.ip.inaddrerrors count of ip inaddrerrors +@ network.ip.forwdatagrams count of ip forwdatagrams +@ network.ip.inunknownprotos count of ip inunknownprotos +@ network.ip.indiscards count of ip indiscards +@ network.ip.indelivers count of ip indelivers +@ network.ip.outrequests count of ip outrequests +@ network.ip.outdiscards count of ip outdiscards +@ network.ip.outnoroutes count of ip outnoroutes +@ network.ip.reasmtimeout count of ip reasmtimeout +@ network.ip.reasmreqds count of ip reasmreqds +@ network.ip.reasmoks count of ip reasmoks +@ network.ip.reasmfails count of ip reasmfails +@ network.ip.fragoks count of ip fragoks +@ network.ip.fragfails count of ip fragfails +@ network.ip.fragcreates count of ip fragcreates +@ network.icmp.inmsgs count of icmp inmsgs +@ network.icmp.inerrors count of icmp inerrors +@ network.icmp.indestunreachs count of icmp indestunreachs +@ network.icmp.intimeexcds count of icmp intimeexcds +@ network.icmp.inparmprobs count of icmp inparmprobs +@ network.icmp.insrcquenchs count of icmp insrcquenchs +@ network.icmp.inredirects count of icmp inredirects +@ network.icmp.inechos count of icmp inechos +@ network.icmp.inechoreps count of icmp inechoreps +@ network.icmp.intimestamps count of icmp intimestamps +@ network.icmp.intimestampreps count of icmp intimestampreps +@ network.icmp.inaddrmasks count of icmp inaddrmasks +@ network.icmp.inaddrmaskreps count of icmp inaddrmaskreps +@ network.icmp.outmsgs count of icmp outmsgs +@ network.icmp.outerrors count of icmp outerrors +@ network.icmp.outdestunreachs count of icmp outdestunreachs +@ network.icmp.outtimeexcds count of icmp outtimeexcds +@ network.icmp.outparmprobs count of icmp outparmprobs +@ network.icmp.outsrcquenchs count of icmp outsrcquenchs +@ network.icmp.outredirects count of icmp outredirects +@ network.icmp.outechos count of icmp outechos +@ network.icmp.outechoreps count of icmp outechoreps +@ network.icmp.outtimestamps count of icmp outtimestamps +@ network.icmp.outtimestampreps count of icmp outtimestampreps +@ network.icmp.outaddrmasks count of icmp outaddrmasks +@ network.icmp.outaddrmaskreps count of icmp outaddrmaskreps +@ network.icmp.incsumerrors count of icmp in checksum errors +@ network.icmpmsg.intype count of icmp message types recvd +@ network.icmpmsg.outtype count of icmp message types sent +@ network.tcp.rtoalgorithm count of tcp rtoalgorithm +@ network.tcp.rtomin count of tcp rtomin +@ network.tcp.rtomax count of tcp rtomax +@ network.tcp.maxconn count of tcp maxconn +@ network.tcp.activeopens count of tcp activeopens +@ network.tcp.passiveopens count of tcp passiveopens +@ network.tcp.attemptfails count of tcp attemptfails +@ network.tcp.estabresets count of tcp estabresets +@ network.tcp.currestab count of tcp currestab +@ network.tcp.insegs count of tcp insegs +@ network.tcp.outsegs count of tcp outsegs +@ network.tcp.retranssegs count of tcp retranssegs +@ network.tcp.inerrs count of tcp inerrs +@ network.tcp.outrsts count of tcp outrsts +@ network.tcp.incsumerrors count of tcp in checksum errors +@ network.tcpconn.established Number of established connections +@ network.tcpconn.syn_sent Number of SYN_SENT connections +@ network.tcpconn.syn_recv Number of SYN_RECV connections +@ network.tcpconn.fin_wait1 Number of FIN_WAIT1 connections +@ network.tcpconn.fin_wait2 Number of FIN_WAIT2 connections +@ network.tcpconn.time_wait Number of TIME_WAIT connections +@ network.tcpconn.close Number of CLOSE connections +@ network.tcpconn.close_wait Number of CLOSE_WAIT connections +@ network.tcpconn.last_ack Number of LAST_ACK connections +@ network.tcpconn.listen Number of LISTEN connections +@ network.tcpconn.closing Number of CLOSING connections +@ network.udp.indatagrams count of udp indatagrams +@ network.udp.noports count of udp noports +@ network.udp.inerrors count of udp inerrors +@ network.udp.outdatagrams count of udp outdatagrams +@ network.udp.recvbuferrors count of udp receive buffer errors +@ network.udp.sndbuferrors count of udp send buffer errors +@ network.udp.incsumerrors count of udp in checksum errors +@ network.udplite.indatagrams count of udplite indatagrams +@ network.udplite.noports count of udplite noports +@ network.udplite.inerrors count of udplite inerrors +@ network.udplite.outdatagrams count of udplite outdatagrams +@ network.udplite.recvbuferrors count of udplite receive buffer errors +@ network.udplite.sndbuferrors count of udplite send buffer errors +@ network.udplite.incsumerrors count of udplite in checksum errors + +@ network.ip.innoroutes Number of IP datagrams discarded due to no routes in forwarding path +@ network.ip.intruncatedpkts Number of IP datagrams discarded due to frame not carrying enough data +@ network.ip.inmcastpkts Number of received IP multicast datagrams +@ network.ip.outmcastpkts Number of sent IP multicast datagrams +@ network.ip.inbcastpkts Number of received IP broadcast datagrams +@ network.ip.outbcastpkts Number of sent IP bradcast datagrams +@ network.ip.inoctets Number of received octets +@ network.ip.outoctets Number of sent octets +@ network.ip.inmcastoctets Number of received IP multicast octets +@ network.ip.outmcastoctets Number of sent IP multicast octets +@ network.ip.inbcastoctets Number of received IP broadcast octets +@ network.ip.outbcastoctets Number of sent IP broadcast octets +@ network.ip.csumerrors Number of IP datagrams with checksum errors +@ network.ip.noectpkts Number of packets received with NOECT +@ network.ip.ect1pkts Number of packets received with ECT(1) +@ network.ip.ect0pkts Number of packets received with ECT(0) +@ network.ip.cepkts Number of packets received with Congestion Experimented + +@ network.tcp.syncookiessent Number of sent SYN cookies +@ network.tcp.syncookiesrecv Number of received SYN cookies +@ network.tcp.syncookiesfailed Number of failed SYN cookies +@ network.tcp.embryonicrsts Number of resets received for embryonic SYN_RECV sockets +@ network.tcp.prunecalled Number of packets pruned from receive queue because of socket buffer overrun +@ network.tcp.rcvpruned Number of packets pruned from receive queue +@ network.tcp.ofopruned Number of packets dropped from out-of-order queue because of socket buffer overrun +@ network.tcp.outofwindowicmps Number of dropped out of window ICMPs +@ network.tcp.lockdroppedicmps Number of dropped ICMP because socket was locked +@ network.tcp.arpfilter Number of arp packets filtered +@ network.tcp.timewaited Number of TCP sockets finished time wait in fast timer +@ network.tcp.timewaitrecycled Number of time wait sockets recycled by time stamp +@ network.tcp.timewaitkilled Number of TCP sockets finished time wait in slow timer +@ network.tcp.pawspassiverejected Number of passive connections rejected because of timestamp +@ network.tcp.pawsactiverejected Number of active connections rejected because of timestamp +@ network.tcp.pawsestabrejected Number of packets rejects in established connections because of timestamp +@ network.tcp.delayedacks Number of delayed acks sent +@ network.tcp.delayedacklocked Number of delayed acks further delayed because of locked socket +@ network.tcp.delayedacklost Number of times quick ack mode was activated times +@ network.tcp.listenoverflows Number of times the listen queue of a socket overflowed +@ network.tcp.listendrops Number of SYNs to LISTEN sockets dropped +@ network.tcp.prequeued Number of packets directly queued to recvmsg prequeue +@ network.tcp.directcopyfrombacklog Number of bytes directly in process context from backlog +@ network.tcp.directcopyfromprequeue Number of bytes directly received in process context from prequeue +@ network.tcp.prequeueddropped Number of packets dropped from prequeue +@ network.tcp.hphits Number of packet headers predicted +@ network.tcp.hphitstouser Number of packets header predicted and directly queued to user +@ network.tcp.pureacks Number of acknowledgments not containing data payload received +@ network.tcp.hpacks Number of predicted acknowledgments +@ network.tcp.renorecovery Number of times recovered from packet loss due to fast retransmit +@ network.tcp.sackrecovery Number of times recovered from packet loss by selective acknowledgements +@ network.tcp.sackreneging Number of bad SACK blocks received +@ network.tcp.fackreorder Number of times detected reordering using FACK +@ network.tcp.sackreorder Number of times detected reordering using SACK +@ network.tcp.renoreorder Number of times detected reordering using reno fast retransmit +@ network.tcp.tsreorder Number of times detected reordering times using time stamp +@ network.tcp.fullundo Number of congestion windows fully recovered without slow start +@ network.tcp.partialundo Number of congestion windows partially recovered using Hoe heuristic +@ network.tcp.dsackundo Number of congestion windows recovered without slow start using DSACK +@ network.tcp.lossundo Number of congestion windows recovered without slow start after partial ack +@ network.tcp.lostretransmit Number of retransmits lost +@ network.tcp.renofailures Number of timeouts after reno fast retransmit +@ network.tcp.sackfailures Number of timeouts after SACK recovery +@ network.tcp.lossfailures Number of timeouts in loss state +@ network.tcp.fastretrans Number of fast retransmits +@ network.tcp.forwardretrans Number of forward retransmits +@ network.tcp.slowstartretrans Number of retransmits in slow start +@ network.tcp.timeouts Number of other TCP timeouts +@ network.tcp.lossprobes Number of sent TCP loss probes +@ network.tcp.lossproberecovery Number of TCP loss probe recoveries +@ network.tcp.renorecoveryfail Number of reno fast retransmits failed +@ network.tcp.sackrecoveryfail Number of SACK retransmits failed +@ network.tcp.schedulerfail Number of times receiver scheduled too late for direct processing +@ network.tcp.rcvcollapsed Number of packets collapsed in receive queue due to low socket buffer +@ network.tcp.dsackoldsent Number of DSACKs sent for old packets +@ network.tcp.dsackofosent Number of DSACKs sent for out of order packets +@ network.tcp.dsackrecv Number of DSACKs received +@ network.tcp.dsackoforecv Number of DSACKs for out of order packets received +@ network.tcp.abortondata Number of connections reset due to unexpected data +@ network.tcp.abortonclose Number of connections reset due to early user close +@ network.tcp.abortonmemory Number of connections aborted due to memory pressure +@ network.tcp.abortontimeout Number of connections aborted due to timeout +@ network.tcp.abortonlinger Number of connections aborted after user close in linger timeout +@ network.tcp.abortfailed Number of times unable to send RST due to no memory +@ network.tcp.memorypressures Numer of times TCP ran low on memory +@ network.tcp.sackdiscard Number of SACKs discarded +@ network.tcp.dsackignoredold Number of ignored old duplicate SACKs +@ network.tcp.dsackignorednoundo Number of ignored duplicate SACKs with undo_marker not set +@ network.tcp.spuriousrtos Number of FRTO's successfully detected spurious RTOs +@ network.tcp.md5notfound Number of times MD5 hash expected but not found +@ network.tcp.md5unexpected Number of times MD5 hash unexpected but found +@ network.tcp.sackshifted Number of SACKs shifted +@ network.tcp.sackmerged Number of SACKs merged +@ network.tcp.sackshiftfallback Number of SACKs fallbacks +@ network.tcp.backlogdrop Number of frames dropped because of full backlog queue +@ network.tcp.minttldrop Number of frames dropped when TTL is under the minimum +@ network.tcp.deferacceptdrop Number of dropped ACK frames when socket is in SYN-RECV state +Due to SYNACK retrans count lower than defer_accept value + +@ network.tcp.iprpfilter Number of packets dropped in input path because of rp_filter settings +@ network.tcp.timewaitoverflow Number of occurences of time wait bucket overflow +@ network.tcp.reqqfulldocookies Number of times a SYNCOOKIE was replied to client +@ network.tcp.reqqfulldrop Number of times a SYN request was dropped due to disabled syncookies +@ network.tcp.retransfail Number of failed tcp_retransmit_skb() calls +@ network.tcp.rcvcoalesce Number of times tried to coalesce the receive queue +@ network.tcp.ofoqueue Number of packets queued in OFO queue +@ network.tcp.ofodrop Number of packets meant to be queued in OFO but dropped due to limits hit +Number of packets meant to be queued in OFO but dropped because socket rcvbuf +limit reached. +@ network.tcp.ofomerge Number of packets in OFO that were merged with other packets +@ network.tcp.challengeack Number of challenge ACKs sent (RFC 5961 3.2) +@ network.tcp.synchallenge Number of challenge ACKs sent in response to SYN packets +@ network.tcp.fastopenactive Number of successful active fast opens +@ network.tcp.fastopenactivefail Number of fast open attempts failed due to remote not accepting it or time outs +@ network.tcp.fastopenpassive Number of successful passive fast opens +@ network.tcp.fastopenpassivefail Number of passive fast open attempts failed +@ network.tcp.fastopenlistenoverflow Number of times the fastopen listen queue overflowed +@ network.tcp.fastopencookiereqd Number of fast open cookies requested +@ network.tcp.spuriousrtxhostqueues Number of times that the fast clone is not yet freed in tcp_transmit_skb() +@ network.tcp.busypollrxpackets Number of low latency application-fetched packets +@ network.tcp.autocorking Number of times stack detected skb was underused and its flush was deferred +@ network.tcp.fromzerowindowadv Number of times window went from zero to non-zero +@ network.tcp.tozerowindowadv Number of times window went from non-zero to zero +@ network.tcp.wantzerowindowadv Number of times zero window announced +@ network.tcp.synretrans Number of SYN-SYN/ACK retransmits +Number of SYN-SYN/ACK retransmits to break down retransmissions in SYN, fast/timeout +retransmits. +@ network.tcp.origdatasent Number of outgoing packets with original data +Excluding retransmission but including data-in-SYN). This counter is different from +TcpOutSegs because TcpOutSegs also tracks pure ACKs. TCPOrigDataSent is +more useful to track the TCP retransmission rate. + +@ pmda.uname identity and type of current system +Identity and type of current system. The concatenation of the values +returned from utsname(2), also similar to uname -a. + +See also the kernel.uname.* metrics + +@ pmda.version build version of Linux PMDA +@ hinv.map.cpu_num logical to physical CPU mapping for each CPU +@ hinv.map.cpu_node logical CPU to NUMA node mapping for each CPU +@ hinv.machine machine name, IP35 if SGI SNIA, else simply linux +@ hinv.cpu.clock clock rate in Mhz for each CPU as reported by /proc/cpuinfo +@ hinv.cpu.vendor manafacturer of each CPU as reported by /proc/cpuinfo +@ hinv.cpu.model model number of each CPU as reported by /proc/cpuinfo +@ hinv.cpu.model_name model name of each CPU as reported by /proc/cpuinfo +@ hinv.cpu.stepping stepping of each CPU as reported by /proc/cpuinfo +@ hinv.cpu.cache primary cache size of each CPU as reported by /proc/cpuinfo +@ hinv.cpu.bogomips bogo mips rating for each CPU as reported by /proc/cpuinfo +@ kernel.all.hz value of HZ (jiffies/second) for the currently running kernel +@ kernel.all.uptime time the current kernel has been running +@ kernel.all.idletime time the current kernel has been idle since boot +@ kernel.all.lastpid most recently allocated process id +@ kernel.all.runnable total number of processes in the (per-CPU) run queues +@ kernel.all.nprocs total number of processes (lightweight) +@ mem.slabinfo.objects.active number of active objects in each cache +@ mem.slabinfo.objects.total total number of objects in each cache +@ mem.slabinfo.objects.size size of individual objects of each cache +@ mem.slabinfo.slabs.active number of active slabs comprising each cache +@ mem.slabinfo.slabs.total total number of slabs comprising each cache +@ mem.slabinfo.slabs.pages_per_slab number of pages in each slab +@ mem.slabinfo.slabs.objects_per_slab number of objects in each slab +@ mem.slabinfo.slabs.total_size total number of bytes allocated for active objects in each slab +@ ipc.sem.max_semmap maximum number of entries in a semaphore map (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_semid maximum number of semaphore identifiers (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_sem maximum number of semaphores in system (from semctl(..,IPC_INFO,..)) +@ ipc.sem.num_undo number of undo structures in system (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_perid maximum number of semaphores per identifier (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_ops maximum number of operations per semop call (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_undoent maximum number of undo entries per process (from semctl(..,IPC_INFO,..)) +@ ipc.sem.sz_semundo size of struct sem_undo (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_semval semaphore maximum value (from semctl(..,IPC_INFO,..)) +@ ipc.sem.max_exit adjust on exit maximum value (from semctl(..,IPC_INFO,..)) +@ ipc.msg.sz_pool size of message pool in kilobytes (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.mapent number of entries in a message map (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.max_msgsz maximum size of a message in bytes (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.max_defmsgq default maximum size of a message queue (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.max_msgqid maximum number of message queue identifiers (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.max_msgseg message segment size (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.num_smsghdr number of system message headers (from msgctl(..,IPC_INFO,..)) +@ ipc.msg.max_seg maximum number of message segments (from msgctl(..,IPC_INFO,..)) +@ ipc.shm.max_segsz maximum shared segment size in bytes (from shmctl(..,IPC_INFO,..)) +@ ipc.shm.min_segsz minimum shared segment size in bytes (from shmctl(..,IPC_INFO,..)) +@ ipc.shm.max_seg maximum number of shared segments in system (from shmctl(..,IPC_INFO,..)) +@ ipc.shm.max_segproc maximum number of shared segments per process (from shmctl(..,IPC_INFO,..)) +@ ipc.shm.max_shmsys maximum amount of shared memory in system in pages (from shmctl(..,IPC_INFO,..)) + +@ vfs.files.count number of in-use file structures +@ vfs.files.free number of available file structures +@ vfs.files.max hard maximum on number of file structures +@ vfs.inodes.count number of in-use inode structures +@ vfs.inodes.free number of available inode structures +@ vfs.dentry.count number of in-use dentry structures +@ vfs.dentry.free number of available dentry structures + +@ sysfs.kernel.uevent_seqnum counter of the number of uevents processed by the udev subsystem diff --git a/src/pmdas/linux/indom.h b/src/pmdas/linux/indom.h new file mode 100644 index 0000000..89f5236 --- /dev/null +++ b/src/pmdas/linux/indom.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * Copyright (c) 2005,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _INDOM_H +#define _INDOM_H + +enum { + CPU_INDOM = 0, /* 0 - percpu */ + DISK_INDOM, /* 1 - disks */ + LOADAVG_INDOM, /* 2 - 1, 5, 15 minute load averages */ + NET_DEV_INDOM, /* 3 - network interfaces */ + PROC_INTERRUPTS_INDOM, /* 4 - interrupt lines -> proc PMDA */ + FILESYS_INDOM, /* 5 - mounted bdev filesystems */ + SWAPDEV_INDOM, /* 6 - swap devices */ + NFS_INDOM, /* 7 - nfs operations */ + NFS3_INDOM, /* 8 - nfs v3 operations */ + PROC_PROC_INDOM, /* 9 - processes */ + PARTITIONS_INDOM, /* 10 - disk partitions */ + SCSI_INDOM, /* 11 - scsi devices */ + SLAB_INDOM, /* 12 - kernel slabs */ + STRINGS_INDOM, /* 13 - string dictionary */ + NFS4_CLI_INDOM, /* 14 - nfs v4 client operations */ + NFS4_SVR_INDOM, /* 15 - nfs n4 server operations */ + QUOTA_PRJ_INDOM, /* 16 - project quota -> xfs PMDA */ + NET_ADDR_INDOM, /* 17 - inet/ipv6 addresses */ + TMPFS_INDOM, /* 18 - tmpfs mounts */ + NODE_INDOM, /* 19 - NUMA nodes */ + PROC_CGROUP_SUBSYS_INDOM, /* 20 - control group subsystems -> proc PMDA */ + PROC_CGROUP_MOUNTS_INDOM, /* 21 - control group mounts -> proc PMDA */ + LV_INDOM, /* 22 - lvm devices */ + ICMPMSG_INDOM, /* 23 - icmp message types */ + DM_INDOM, /* 24 device mapper devices */ + + NUM_INDOMS /* one more than highest numbered cluster */ +}; + +extern pmInDom linux_indom(int); +#define INDOM(i) linux_indom(i) + +extern pmdaIndom *linux_pmda_indom(int); +#define PMDAINDOM(i) linux_pmda_indom(i) + +/* + * Optional path prefix for all stats files, used for testing. + */ +extern char *linux_statspath; +extern FILE *linux_statsfile(const char *, char *, int); + +/* + * static string dictionary - one copy of oft-repeated strings; + * implemented using STRINGS_INDOM and pmdaCache(3) routines. + */ +char *linux_strings_lookup(int); +int linux_strings_insert(const char *); + +#endif /* _INDOM_H */ diff --git a/src/pmdas/linux/interrupts.c b/src/pmdas/linux/interrupts.c new file mode 100644 index 0000000..47377e8 --- /dev/null +++ b/src/pmdas/linux/interrupts.c @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2011 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "filesys.h" +#include "clusters.h" +#include "interrupts.h" +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include + +typedef struct { + unsigned int id; /* becomes PMID item number */ + char *name; /* becomes PMNS sub-component */ + char *text; /* one-line metric help text */ + unsigned long long *values; /* per-CPU values for this counter */ +} interrupt_t; + +static unsigned int cpu_count; +static int *online_cpumap; /* maps input columns to CPU IDs */ +static unsigned int lines_count; +static interrupt_t *interrupt_lines; +static unsigned int other_count; +static interrupt_t *interrupt_other; + +static __pmnsTree *interrupt_tree; +unsigned int irq_err_count; + +static void +update_lines_pmns(int domain, unsigned int item, unsigned int id) +{ + char entry[128]; + pmID pmid = pmid_build(domain, CLUSTER_INTERRUPT_LINES, item); + + snprintf(entry, sizeof(entry), "kernel.percpu.interrupts.line%d", id); + __pmAddPMNSNode(interrupt_tree, pmid, entry); +} + +static void +update_other_pmns(int domain, unsigned int item, const char *name) +{ + char entry[128]; + pmID pmid = pmid_build(domain, CLUSTER_INTERRUPT_OTHER, item); + + snprintf(entry, sizeof(entry), "kernel.percpu.interrupts.%s", name); + __pmAddPMNSNode(interrupt_tree, pmid, entry); +} + +static int +map_online_cpus(char *buffer) +{ + unsigned long i = 0, cpuid; + char *s, *end; + + for (s = buffer; *s != '\0'; s++) { + if (!isdigit((int)*s)) + continue; + cpuid = strtoul(s, &end, 10); + if (end == s) + break; + online_cpumap[i++] = cpuid; + s = end; + } + return i; +} + +static int +column_to_cpuid(int column) +{ + int i; + + if (online_cpumap[column] == column) + return column; + for (i = 0; i < cpu_count; i++) + if (online_cpumap[i] == column) + return i; + return 0; +} + +static char * +extract_values(char *buffer, unsigned long long *values, int ncolumns) +{ + unsigned long i, value, cpuid; + char *s = buffer, *end = NULL; + + for (i = 0; i < ncolumns; i++) { + value = strtoul(s, &end, 10); + if (*end != ' ') + return NULL; + s = end; + cpuid = column_to_cpuid(i); + values[cpuid] = value; + } + return end; +} + +/* Create oneline help text - remove duplicates and end-of-line marker */ +static char * +oneline_reformat(char *buf) +{ + char *result, *start, *end; + + /* position end marker, and skip over whitespace at the start */ + for (start = end = buf; *end != '\n' && *end != '\0'; end++) + if (isspace((int)*start) && isspace((int)*end)) + start = end+1; + *end = '\0'; + + /* squash duplicate whitespace and remove trailing whitespace */ + for (result = start; *result != '\0'; result++) { + if (isspace((int)result[0]) && (isspace((int)result[1]) || result[1] == '\0')) { + memmove(&result[0], &result[1], end - &result[0]); + result--; + } + } + return start; +} + +static void +initialise_interrupt(interrupt_t *ip, unsigned int id, char *s, char *end) +{ + ip->id = id; + ip->name = strdup(s); + if (end) + ip->text = strdup(oneline_reformat(end)); +} + +static int +extend_interrupts(interrupt_t **interp, unsigned int *countp) +{ + int cnt = cpu_count * sizeof(unsigned long long); + unsigned long long *values = malloc(cnt); + interrupt_t *interrupt = *interp; + int count = *countp + 1; + + if (!values) + return 0; + + interrupt = realloc(interrupt, count * sizeof(interrupt_t)); + if (!interrupt) { + free(values); + return 0; + } + interrupt[count-1].values = values; + *interp = interrupt; + *countp = count; + return 1; +} + +static char * +extract_interrupt_name(char *buffer, char **suffix) +{ + char *s = buffer, *end; + + while (isspace((int)*s)) /* find start of name */ + s++; + for (end = s; *end && isalnum((int)*end); end++) { } + *end = '\0'; /* mark end of name */ + *suffix = end + 1; /* mark values start */ + return s; +} + +static int +extract_interrupt_lines(char *buffer, int ncolumns, int nlines) +{ + unsigned long id; + char *name, *end, *values; + int resize = (nlines >= lines_count); + + name = extract_interrupt_name(buffer, &values); + id = strtoul(name, &end, 10); + if (*end != '\0') + return 0; + if (resize && !extend_interrupts(&interrupt_lines, &lines_count)) + return 0; + end = extract_values(values, interrupt_lines[nlines].values, ncolumns); + if (resize) + initialise_interrupt(&interrupt_lines[nlines], id, name, end); + return 1; +} + +static int +extract_interrupt_errors(char *buffer) +{ + return (sscanf(buffer, " ERR: %u", &irq_err_count) == 1 || + sscanf(buffer, "Err: %u", &irq_err_count) == 1 || + sscanf(buffer, "BAD: %u", &irq_err_count) == 1); +} + +static int +extract_interrupt_misses(char *buffer) +{ + unsigned int irq_mis_count; /* not exported */ + return sscanf(buffer, " MIS: %u", &irq_mis_count) == 1; +} + +static int +extract_interrupt_other(char *buffer, int ncolumns, int nlines) +{ + char *name, *end, *values; + int resize = (nlines >= other_count); + + name = extract_interrupt_name(buffer, &values); + if (resize && !extend_interrupts(&interrupt_other, &other_count)) + return 0; + end = extract_values(values, interrupt_other[nlines].values, ncolumns); + if (resize) + initialise_interrupt(&interrupt_other[nlines], nlines, name, end); + return 1; +} + +int +refresh_interrupt_values(void) +{ + FILE *fp; + char buf[4096]; + int i, ncolumns; + + if (cpu_count == 0) { + long ncpus = sysconf(_SC_NPROCESSORS_CONF); + online_cpumap = malloc(ncpus * sizeof(int)); + if (!online_cpumap) + return -oserror(); + cpu_count = ncpus; + } + memset(online_cpumap, 0, cpu_count * sizeof(int)); + + if ((fp = linux_statsfile("/proc/interrupts", buf, sizeof(buf))) == NULL) + return -oserror(); + + /* first parse header, which maps online CPU number to column number */ + if (fgets(buf, sizeof(buf), fp)) { + ncolumns = map_online_cpus(buf); + } else { + fclose(fp); + return -EINVAL; /* unrecognised file format */ + } + + /* next we parse each interrupt line row (starting with a digit) */ + i = 0; + while (fgets(buf, sizeof(buf), fp)) + if (!extract_interrupt_lines(buf, ncolumns, i++)) + break; + + /* parse other per-CPU interrupt counter rows (starts non-digit) */ + i = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (extract_interrupt_errors(buf)) + continue; + if (extract_interrupt_misses(buf)) + continue; + if (!extract_interrupt_other(buf, ncolumns, i++)) + break; + } + + fclose(fp); + return 0; +} + +static int +refresh_interrupts(pmdaExt *pmda, __pmnsTree **tree) +{ + int i, sts, dom = pmda->e_domain; + + if (interrupt_tree) { + *tree = interrupt_tree; + } else if ((sts = __pmNewPMNS(&interrupt_tree)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create interrupt names: %s\n", + pmProgname, pmErrStr(sts)); + *tree = NULL; + } else if ((sts = refresh_interrupt_values()) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to update interrupt values: %s\n", + pmProgname, pmErrStr(sts)); + *tree = NULL; + } else { + for (i = 0; i < lines_count; i++) + update_lines_pmns(dom, i, interrupt_lines[i].id); + for (i = 0; i < other_count; i++) + update_other_pmns(dom, i, interrupt_other[i].name); + *tree = interrupt_tree; + return 1; + } + return 0; +} + +int +interrupts_fetch(int cluster, int item, unsigned int inst, pmAtomValue *atom) +{ + if (inst >= cpu_count) + return PM_ERR_INST; + + switch (cluster) { + case CLUSTER_INTERRUPT_LINES: + if (item > lines_count) + return PM_ERR_PMID; + atom->ull = interrupt_lines[item].values[inst]; + return 1; + case CLUSTER_INTERRUPT_OTHER: + if (item > other_count) + return PM_ERR_PMID; + atom->ull = interrupt_other[item].values[inst]; + return 1; + } + return PM_ERR_PMID; +} + +/* + * Create a new metric table entry based on an existing one. + */ +static void +refresh_metrictable(pmdaMetric *source, pmdaMetric *dest, int id) +{ + int domain = pmid_domain(source->m_desc.pmid); + int cluster = pmid_cluster(source->m_desc.pmid); + + memcpy(dest, source, sizeof(pmdaMetric)); + dest->m_desc.pmid = pmid_build(domain, cluster, id); + + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, "interrupts refresh_metrictable: (%p -> %p) " + "metric ID dup: %d.%d.%d -> %d.%d.%d\n", + source, dest, domain, cluster, + pmid_item(source->m_desc.pmid), domain, cluster, id); +} + +/* + * Needs to answer the question: how much extra space needs to be + * allocated in the metric table for (dynamic) interrupt metrics"? + * Return value is the number of additional entries/trees needed. + */ +static void +size_metrictable(int *total, int *trees) +{ + *total = 2; /* lines and other */ + *trees = lines_count > other_count ? lines_count : other_count; + + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, "interrupts size_metrictable: %d total x %d trees\n", + *total, *trees); +} + +static int +interrupts_text(pmdaExt *pmda, pmID pmid, int type, char **buf) +{ + int item = pmid_item(pmid); + int cluster = pmid_cluster(pmid); + + switch (cluster) { + case CLUSTER_INTERRUPT_LINES: + if (item > lines_count) + return PM_ERR_PMID; + if (interrupt_lines[item].text == NULL) + return PM_ERR_TEXT; + *buf = interrupt_lines[item].text; + return 0; + case CLUSTER_INTERRUPT_OTHER: + if (item > other_count) + return PM_ERR_PMID; + if (interrupt_other[item].text == NULL) + return PM_ERR_TEXT; + *buf = interrupt_other[item].text; + return 0; + } + return PM_ERR_PMID; +} + +void +interrupts_init(pmdaMetric *metrictable, int nmetrics) +{ + int set[] = { CLUSTER_INTERRUPT_LINES, CLUSTER_INTERRUPT_OTHER }; + + pmdaDynamicPMNS("kernel.percpu.interrupts", + set, sizeof(set)/sizeof(int), + refresh_interrupts, interrupts_text, + refresh_metrictable, size_metrictable, + metrictable, nmetrics); +} diff --git a/src/pmdas/linux/interrupts.h b/src/pmdas/linux/interrupts.h new file mode 100644 index 0000000..b8a0336 --- /dev/null +++ b/src/pmdas/linux/interrupts.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +extern unsigned int irq_err_count; + +extern void interrupts_init(pmdaMetric *, int); +extern int refresh_interrupt_values(void); +extern int interrupts_fetch(int, int, unsigned int, pmAtomValue *); diff --git a/src/pmdas/linux/linux_table.c b/src/pmdas/linux/linux_table.c new file mode 100644 index 0000000..d04f454 --- /dev/null +++ b/src/pmdas/linux/linux_table.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "linux_table.h" + +extern int linux_table_lookup(const char *field, struct linux_table *table, uint64_t *val); +extern struct linux_table *linux_table_clone(struct linux_table *table); +extern int linux_table_scan(FILE *fp, struct linux_table *table); + +inline int +linux_table_lookup(const char *field, struct linux_table *table, uint64_t *val) +{ + struct linux_table *t; + + for (t=table; t && t->field; t++) { + if (strncmp(field, t->field, t->field_len) == 0) { + if (t->valid) { + *val = t->val; + return 1; + } + /* Invalid */ + return 0; + } + } + + fprintf(stderr, "Warning: linux_table_lookup failed for \"%s\"\n", field); + return 0; +} + +inline struct linux_table * +linux_table_clone(struct linux_table *table) +{ + struct linux_table *ret; + struct linux_table *t; + int len; + + if (!table) + return NULL; + for (len=1, t=table; t->field; t++) + len++; + ret = (struct linux_table *)malloc(len * sizeof(struct linux_table)); + if (!ret) + return NULL; + memcpy(ret, table, len * sizeof(struct linux_table)); + + /* Initialize the table */ + for (t=ret; t && t->field; t++) { + if (!t->field_len) + t->field_len = strlen(t->field); + t->valid = LINUX_TABLE_INVALID; + } + + return ret; +} + +inline int +linux_table_scan(FILE *fp, struct linux_table *table) +{ + char *p; + struct linux_table *t; + char buf[1024]; + int ret = 0; + + while(fgets(buf, sizeof(buf), fp) != NULL) { + for (t=table; t && t->field; t++) { + if ((p = strstr(buf, t->field)) != NULL) { + /* first digit after the matched field */ + for (p += t->field_len; *p; p++) { + if (isdigit((int)*p)) + break; + } + if (isdigit((int)*p)) { + t->this = strtoul(p, NULL, 10); + t->valid = LINUX_TABLE_VALID; + ret++; + break; + } + } + } + } + + /* calculate current value, accounting for counter wrap */ + for (t=table; t && t->field; t++) { + if (t->maxval == 0) + /* instantaneous value */ + t->val = t->this; + else { + /* counter value */ + if (t->this >= t->prev) + t->val += t->this - t->prev; + else + t->val += t->this + (t->maxval - t->prev); + t->prev = t->this; + } + } + + return ret; +} diff --git a/src/pmdas/linux/linux_table.h b/src/pmdas/linux/linux_table.h new file mode 100644 index 0000000..3900690 --- /dev/null +++ b/src/pmdas/linux/linux_table.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _LINUX_TABLE_H +#define _LINUX_TABLE_H +/* + * scans linux style /proc tables, e.g. : + * + * numa_hit 266809 + * numa_miss 0 + * numa_foreign 0 + * interleave_hit 0 + * local_node 265680 + * other_node 1129 + * + * Value is a counter that wraps at maxval, + * unless maxval is 0, in which case the + * value is treated as instantaneous and no + * wrap detection is attempted. + * + * Tables are typically declared as a static array, and + * then allocated dynamically with linux_table_clone(). + * e.g. : + * + * static struct linux_table numa_meminfo_table[] = { + * { "numa_hit", 0xffffffffffffffff }, + * { "numa_miss", 0xffffffffffffffff }, + * { "numa_foreign", 0xffffffffffffffff }, + * { "interleave_hit", 0xffffffffffffffff }, + * { "local_node", 0xffffffffffffffff }, + * { "other_node", 0xffffffffffffffff }, + * { NULL }; + * }; + */ + +enum { + LINUX_TABLE_INVALID, + LINUX_TABLE_VALID +}; + +struct linux_table { + char *field; + uint64_t maxval; + uint64_t val; + uint64_t this; + uint64_t prev; + int field_len; + int valid; +}; + +extern int linux_table_lookup(const char *field, struct linux_table *table, uint64_t *val); +extern struct linux_table *linux_table_clone(struct linux_table *table); +extern int linux_table_scan(FILE *fp, struct linux_table *table); + +#endif /* _LINUX_TABLE_H */ diff --git a/src/pmdas/linux/msg_limits.c b/src/pmdas/linux/msg_limits.c new file mode 100644 index 0000000..afac20e --- /dev/null +++ b/src/pmdas/linux/msg_limits.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * This code contributed by Mike Mason + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define __USE_GNU 1 /* required for IPC_INFO define */ +#include +#include + +#include "pmapi.h" +#include "msg_limits.h" + +int +refresh_msg_limits(msg_limits_t *msg_limits) +{ + static struct msginfo msginfo; + static int started; + + if (!started) { + started = 1; + memset(msg_limits, 0, sizeof(msg_limits_t)); + } + + if (msgctl(0, IPC_INFO, (struct msqid_ds *) &msginfo) < 0) { + return -oserror(); + } + + msg_limits->msgpool = msginfo.msgpool; + msg_limits->msgmap = msginfo.msgmap; + msg_limits->msgmax = msginfo.msgmax; + msg_limits->msgmnb = msginfo.msgmnb; + msg_limits->msgmni = msginfo.msgmni; + msg_limits->msgssz = msginfo.msgssz; + msg_limits->msgtql = msginfo.msgtql; + msg_limits->msgseg = msginfo.msgseg; + + /* success */ + return 0; +} diff --git a/src/pmdas/linux/msg_limits.h b/src/pmdas/linux/msg_limits.h new file mode 100644 index 0000000..06fb7c5 --- /dev/null +++ b/src/pmdas/linux/msg_limits.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This code contributed by Mike Mason (mmlnx@us.ibm.com) + */ + +typedef struct { + unsigned int msgpool; /* size of message pool (kbytes) */ + unsigned int msgmap; /* # of entries in message map */ + unsigned int msgmax; /* maximum size of a message */ + unsigned int msgmnb; /* default maximum size of message queue */ + unsigned int msgmni; /* maximum # of message queue identifiers */ + unsigned int msgssz; /* message segment size */ + unsigned int msgtql; /* # of system message headers */ + unsigned int msgseg; /* maximum # of message segments */ +} msg_limits_t; + +extern int refresh_msg_limits(msg_limits_t*); + diff --git a/src/pmdas/linux/numa_meminfo.c b/src/pmdas/linux/numa_meminfo.c new file mode 100644 index 0000000..22d8351 --- /dev/null +++ b/src/pmdas/linux/numa_meminfo.c @@ -0,0 +1,137 @@ +/* + * Linux NUMA meminfo metrics cluster from sysfs + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2009 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "linux_table.h" +#include "proc_cpuinfo.h" +#include "proc_stat.h" +#include "numa_meminfo.h" + +/* sysfs file for numa meminfo */ +static struct linux_table numa_meminfo_table[] = { + { field: "MemTotal:", maxval: 0x0 }, + { field: "MemFree:", maxval: 0x0 }, + { field: "MemUsed:", maxval: 0x0 }, + { field: "Active:", maxval: 0x0 }, + { field: "Inactive:", maxval: 0x0 }, + { field: "Active(anon):", maxval: 0x0 }, + { field: "Inactive(anon):", maxval: 0x0 }, + { field: "Active(file):", maxval: 0x0 }, + { field: "Inactive(file):", maxval: 0x0 }, + { field: "HighTotal:", maxval: 0x0 }, + { field: "HighFree:", maxval: 0x0 }, + { field: "LowTotal:", maxval: 0x0 }, + { field: "LowFree:", maxval: 0x0 }, + { field: "Unevictable:", maxval: 0x0 }, + { field: "Mlocked:", maxval: 0x0 }, + { field: "Dirty:", maxval: 0x0 }, + { field: "Writeback:", maxval: 0x0 }, + { field: "FilePages:", maxval: 0x0 }, + { field: "Mapped:", maxval: 0x0 }, + { field: "AnonPages:", maxval: 0x0 }, + { field: "Shmem:", maxval: 0x0 }, + { field: "KernelStack:", maxval: 0x0 }, + { field: "PageTables:", maxval: 0x0 }, + { field: "NFS_Unstable:", maxval: 0x0 }, + { field: "Bounce:", maxval: 0x0 }, + { field: "WritebackTmp:", maxval: 0x0 }, + { field: "Slab:", maxval: 0x0 }, + { field: "SReclaimable:", maxval: 0x0 }, + { field: "SUnreclaim:", maxval: 0x0 }, + { field: "HugePages_Total:", maxval: 0x0 }, + { field: "HugePages_Free:", maxval: 0x0 }, + { field: "HugePages_Surp:", maxval: 0x0 }, + { field: NULL } +}; + +/* sysfs file for numastat */ +static struct linux_table numa_memstat_table[] = { + { field: "numa_hit", maxval: ULONGLONG_MAX }, + { field: "numa_miss", maxval: ULONGLONG_MAX }, + { field: "numa_foreign", maxval: ULONGLONG_MAX }, + { field: "interleave_hit", maxval: ULONGLONG_MAX }, + { field: "local_node", maxval: ULONGLONG_MAX }, + { field: "other_node", maxval: ULONGLONG_MAX }, + { field: NULL } +}; + +int refresh_numa_meminfo(numa_meminfo_t *numa_meminfo, proc_cpuinfo_t *proc_cpuinfo, proc_stat_t *proc_stat) +{ + int i; + FILE *fp; + pmdaIndom *idp = PMDAINDOM(NODE_INDOM); + static int started; + + /* First time only */ + if (!started) { + refresh_proc_stat(proc_cpuinfo, proc_stat); + + if (!numa_meminfo->node_info) /* may have allocated this, but failed below */ + numa_meminfo->node_info = (nodeinfo_t *)calloc(idp->it_numinst, sizeof(nodeinfo_t)); + if (!numa_meminfo->node_info) { + fprintf(stderr, "%s: error allocating numa node_info: %s\n", + __FUNCTION__, osstrerror()); + return -1; + } + + for (i = 0; i < idp->it_numinst; i++) { + numa_meminfo->node_info[i].meminfo = linux_table_clone(numa_meminfo_table); + if (!numa_meminfo->node_info[i].meminfo) { + fprintf(stderr, "%s: error allocating meminfo: %s\n", + __FUNCTION__, osstrerror()); + return -1; + } + numa_meminfo->node_info[i].memstat = linux_table_clone(numa_memstat_table); + if (!numa_meminfo->node_info[i].memstat) { + fprintf(stderr, "%s: error allocating memstat: %s\n", + __FUNCTION__, osstrerror()); + return -1; + } + } + + numa_meminfo->node_indom = idp; + started = 1; + } + + /* Refresh */ + for (i = 0; i < idp->it_numinst; i++) { + char buf[MAXPATHLEN]; + + snprintf(buf, sizeof(buf), "%s/sys/devices/system/node/node%d/meminfo", + linux_statspath, i); + if ((fp = fopen(buf, "r")) != NULL) { + linux_table_scan(fp, numa_meminfo->node_info[i].meminfo); + fclose(fp); + } + + snprintf(buf, sizeof(buf), "%s/sys/devices/system/node/node%d/numastat", + linux_statspath, i); + if ((fp = fopen(buf, "r")) != NULL) { + linux_table_scan(fp, numa_meminfo->node_info[i].memstat); + fclose(fp); + } + } + + return 0; +} diff --git a/src/pmdas/linux/numa_meminfo.h b/src/pmdas/linux/numa_meminfo.h new file mode 100644 index 0000000..22c1289 --- /dev/null +++ b/src/pmdas/linux/numa_meminfo.h @@ -0,0 +1,32 @@ +/* + * Linux NUMA meminfo metrics cluster from sysfs + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2009 Silicon Graphics Inc., All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Information from /sys/devices/node/node[0-9]+/meminfo and numastat + */ +typedef struct { + struct linux_table *meminfo; + struct linux_table *memstat; +} nodeinfo_t; + +typedef struct { + nodeinfo_t *node_info; + pmdaIndom *node_indom; +} numa_meminfo_t; + +extern int refresh_numa_meminfo(numa_meminfo_t *, proc_cpuinfo_t *, proc_stat_t *); + diff --git a/src/pmdas/linux/pmda.c b/src/pmdas/linux/pmda.c new file mode 100644 index 0000000..73d961a --- /dev/null +++ b/src/pmdas/linux/pmda.c @@ -0,0 +1,5807 @@ +/* + * Linux PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2007-2011 Aconex. All Rights Reserved. + * Copyright (c) 2002 International Business Machines Corp. + * Copyright (c) 2000,2004,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#undef LINUX /* defined in NSS/NSPR headers as something different, which we do not need. */ +#include "domain.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "convert.h" +#include "clusters.h" +#include "indom.h" + +#include "proc_cpuinfo.h" +#include "proc_stat.h" +#include "proc_meminfo.h" +#include "proc_loadavg.h" +#include "proc_net_dev.h" +#include "filesys.h" +#include "swapdev.h" +#include "getinfo.h" +#include "proc_net_rpc.h" +#include "proc_net_sockstat.h" +#include "proc_net_tcp.h" +#include "proc_partitions.h" +#include "proc_net_netstat.h" +#include "proc_net_snmp.h" +#include "proc_scsi.h" +#include "proc_slabinfo.h" +#include "proc_uptime.h" +#include "sem_limits.h" +#include "msg_limits.h" +#include "shm_limits.h" +#include "proc_sys_fs.h" +#include "proc_vmstat.h" +#include "sysfs_kernel.h" +#include "linux_table.h" +#include "numa_meminfo.h" +#include "interrupts.h" +#include "devmapper.h" + +static proc_stat_t proc_stat; +static proc_meminfo_t proc_meminfo; +static proc_loadavg_t proc_loadavg; +static proc_net_rpc_t proc_net_rpc; +static proc_net_tcp_t proc_net_tcp; +static proc_net_sockstat_t proc_net_sockstat; +static struct utsname kernel_uname; +static char uname_string[sizeof(kernel_uname)]; +static proc_scsi_t proc_scsi; +static dev_mapper_t dev_mapper; +static proc_cpuinfo_t proc_cpuinfo; +static proc_slabinfo_t proc_slabinfo; +static sem_limits_t sem_limits; +static msg_limits_t msg_limits; +static shm_limits_t shm_limits; +static proc_uptime_t proc_uptime; +static proc_sys_fs_t proc_sys_fs; +static sysfs_kernel_t sysfs_kernel; +static numa_meminfo_t numa_meminfo; + +static int _isDSO = 1; /* =0 I am a daemon */ +static char *username; + +/* globals */ +size_t _pm_system_pagesize; /* for hinv.pagesize and used elsewhere */ +int _pm_have_proc_vmstat; /* if /proc/vmstat is available */ +int _pm_intr_size; /* size in bytes of interrupt sum count metric */ +int _pm_ctxt_size; /* size in bytes of context switch count metric */ +int _pm_cputime_size; /* size in bytes of most of the cputime metrics */ +int _pm_idletime_size; /* size in bytes of the idle cputime metric */ +proc_vmstat_t _pm_proc_vmstat; +proc_net_snmp_t _pm_proc_net_snmp; +pmdaInstid _pm_proc_net_snmp_indom_id[NR_ICMPMSG_COUNTERS]; +proc_net_netstat_t _pm_proc_net_netstat; + +/* + * Metric Instance Domains (statically initialized ones only) + */ +static pmdaInstid loadavg_indom_id[] = { + { 1, "1 minute" }, { 5, "5 minute" }, { 15, "15 minute" } +}; + +static pmdaInstid nfs_indom_id[] = { + { 0, "null" }, + { 1, "getattr" }, + { 2, "setattr" }, + { 3, "root" }, + { 4, "lookup" }, + { 5, "readlink" }, + { 6, "read" }, + { 7, "wrcache" }, + { 8, "write" }, + { 9, "create" }, + { 10, "remove" }, + { 11, "rename" }, + { 12, "link" }, + { 13, "symlink" }, + { 14, "mkdir" }, + { 15, "rmdir" }, + { 16, "readdir" }, + { 17, "statfs" } +}; + +static pmdaInstid nfs3_indom_id[] = { + { 0, "null" }, + { 1, "getattr" }, + { 2, "setattr" }, + { 3, "lookup" }, + { 4, "access" }, + { 5, "readlink" }, + { 6, "read" }, + { 7, "write" }, + { 8, "create" }, + { 9, "mkdir" }, + { 10, "symlink" }, + { 11, "mknod" }, + { 12, "remove" }, + { 13, "rmdir" }, + { 14, "rename" }, + { 15, "link" }, + { 16, "readdir" }, + { 17, "readdir+" }, + { 18, "statfs" }, + { 19, "fsinfo" }, + { 20, "pathconf" }, + { 21, "commit" } +}; + +static pmdaInstid nfs4_cli_indom_id[] = { + { 0, "null" }, + { 1, "read" }, + { 2, "write" }, + { 3, "commit" }, + { 4, "open" }, + { 5, "open_conf" }, + { 6, "open_noat" }, + { 7, "open_dgrd" }, + { 8, "close" }, + { 9, "setattr" }, + { 10, "fsinfo" }, + { 11, "renew" }, + { 12, "setclntid" }, + { 13, "confirm" }, + { 14, "lock" }, + { 15, "lockt" }, + { 16, "locku" }, + { 17, "access" }, + { 18, "getattr" }, + { 19, "lookup" }, + { 20, "lookup_root" }, + { 21, "remove" }, + { 22, "rename" }, + { 23, "link" }, + { 24, "symlink" }, + { 25, "create" }, + { 26, "pathconf" }, + { 27, "statfs" }, + { 28, "readlink" }, + { 29, "readdir" }, + { 30, "server_caps" }, + { 31, "delegreturn" }, + { 32, "getacl" }, + { 33, "setacl" }, + { 34, "fs_locatns" }, +}; + +static pmdaInstid nfs4_svr_indom_id[] = { + { 0, "null" }, + { 1, "op0-unused" }, + { 2, "op1-unused"}, + { 3, "minorversion"}, /* future use */ + { 4, "access" }, + { 5, "close" }, + { 6, "commit" }, + { 7, "create" }, + { 8, "delegpurge" }, + { 9, "delegreturn" }, + { 10, "getattr" }, + { 11, "getfh" }, + { 12, "link" }, + { 13, "lock" }, + { 14, "lockt" }, + { 15, "locku" }, + { 16, "lookup" }, + { 17, "lookup_root" }, + { 18, "nverify" }, + { 19, "open" }, + { 20, "openattr" }, + { 21, "open_conf" }, + { 22, "open_dgrd" }, + { 23, "putfh" }, + { 24, "putpubfh" }, + { 25, "putrootfh" }, + { 26, "read" }, + { 27, "readdir" }, + { 28, "readlink" }, + { 29, "remove" }, + { 30, "rename" }, + { 31, "renew" }, + { 32, "restorefh" }, + { 33, "savefh" }, + { 34, "secinfo" }, + { 35, "setattr" }, + { 36, "setcltid" }, + { 37, "setcltidconf" }, + { 38, "verify" }, + { 39, "write" }, + { 40, "rellockowner" }, +}; + +static pmdaIndom indomtab[] = { + { CPU_INDOM, 0, NULL }, + { DISK_INDOM, 0, NULL }, /* cached */ + { LOADAVG_INDOM, 3, loadavg_indom_id }, + { NET_DEV_INDOM, 0, NULL }, + { PROC_INTERRUPTS_INDOM, 0, NULL }, /* deprecated */ + { FILESYS_INDOM, 0, NULL }, + { SWAPDEV_INDOM, 0, NULL }, + { NFS_INDOM, NR_RPC_COUNTERS, nfs_indom_id }, + { NFS3_INDOM, NR_RPC3_COUNTERS, nfs3_indom_id }, + { PROC_PROC_INDOM, 0, NULL }, /* migrated to the proc PMDA */ + { PARTITIONS_INDOM, 0, NULL }, /* cached */ + { SCSI_INDOM, 0, NULL }, + { SLAB_INDOM, 0, NULL }, + { STRINGS_INDOM, 0, NULL }, + { NFS4_CLI_INDOM, NR_RPC4_CLI_COUNTERS, nfs4_cli_indom_id }, + { NFS4_SVR_INDOM, NR_RPC4_SVR_COUNTERS, nfs4_svr_indom_id }, + { QUOTA_PRJ_INDOM, 0, NULL }, /* migrated to the xfs PMDA */ + { NET_ADDR_INDOM, 0, NULL }, + { TMPFS_INDOM, 0, NULL }, + { NODE_INDOM, 0, NULL }, + { PROC_CGROUP_SUBSYS_INDOM, 0, NULL }, + { PROC_CGROUP_MOUNTS_INDOM, 0, NULL }, + { LV_INDOM, 0, NULL }, + { ICMPMSG_INDOM, NR_ICMPMSG_COUNTERS, _pm_proc_net_snmp_indom_id }, + { DM_INDOM, 0, NULL }, /* cached */ +}; + + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +static pmdaMetric metrictab[] = { + +/* + * /proc/stat cluster + */ + +/* kernel.percpu.cpu.user */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,0), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.nice */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,1), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.sys */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,2), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.idle */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,3), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.wait.total */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,30), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.intr */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,31), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.irq.soft */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,56), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.irq.hard */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,57), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.steal */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,58), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.guest */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,61), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.percpu.cpu.vuser */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,76), KERNEL_UTYPE, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + +/* kernel.pernode.cpu.user */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,62), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.nice */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,63), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.sys */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,64), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.idle */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,65), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.wait.total */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,69), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.intr */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,66), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.irq.soft */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,70), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.irq.hard */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,71), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.steal */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,67), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.guest */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,68), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.pernode.cpu.vuser */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,77), KERNEL_UTYPE, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.dev.read */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,4), KERNEL_ULONG, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.write */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,5), KERNEL_ULONG, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.blkread */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,6), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.blkwrite */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,7), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.avactive */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,46), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.dev.aveq */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,47), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.dev.read_merge */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,49), KERNEL_ULONG, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.write_merge */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,50), KERNEL_ULONG, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.scheduler */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,59), PM_TYPE_STRING, DISK_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* disk.dev.read_rawactive */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,72), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.dev.write_rawactive */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,73), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.all.avactive */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.all.aveq */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.all.read_merge */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.write_merge */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.read_rawactive */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.all.read_rawactive */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* swap.pagesin */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* swap.pagesout */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* swap.in */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* swap.out */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* kernel.all.intr */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,12), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* kernel.all.pswitch */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,13), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* kernel.all.sysfork */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,14), KERNEL_ULONG, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* kernel.all.cpu.user */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,20), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.nice */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,21), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.sys */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,22), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.idle */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,23), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.intr */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,34), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.wait.total */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,35), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.irq.soft */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,53), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.irq.hard */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,54), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.steal */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,55), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.guest */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,60), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* kernel.all.cpu.vuser */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,78), KERNEL_UTYPE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + +/* disk.all.read */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,24), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.write */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.blkread */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.blkwrite */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.total */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,28), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.dev.blktotal */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,36), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.total */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.all.blktotal */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* hinv.ncpu */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* hinv.ndisk */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* hinv.nnode */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* kernel.all.hz */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,48), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE) }, }, + +/* + * /proc/uptime cluster + * Uptime modified and idletime added by Mike Mason + */ + +/* kernel.all.uptime */ + { NULL, + { PMDA_PMID(CLUSTER_UPTIME,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, }, + +/* kernel.all.idletime */ + { NULL, + { PMDA_PMID(CLUSTER_UPTIME,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, }, + +/* + * /proc/meminfo cluster + */ + +/* mem.physmem */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.used */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.free */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.shared */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.bufmem */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.cached */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.active */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.inactive */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.swapCached */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.highTotal */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.highFree */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.lowTotal */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.lowFree */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,19), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.swapTotal */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.swapFree */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,21), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.dirty */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.writeback */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.mapped */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,24), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.slab */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.committed_AS */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.pageTables */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.reverseMaps */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.cache_clean */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.anonpages */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.commitLimit */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.bounce */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.NFS_Unstable */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.slabReclaimable */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.slabUnreclaimable */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.active_anon */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.inactive_anon */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.active_file */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,38), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.inactive_file */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,39), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.unevictable */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,40), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.mlocked */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,41), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.shmem */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,42), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.kernelStack */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,43), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.hugepagesTotal */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.util.hugepagesFree */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.util.hugepagesRsvd */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.util.hugepagesSurp */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,47), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.util.directMap4k */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.directMap2M */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,49), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.vmallocTotal */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,50), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.vmallocUsed */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.vmallocChunk */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.mmap_copy */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.quicklists */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.corrupthardware */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.mmap_copy */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.directMap1G */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.util.available */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.total */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,0), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.free */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,1), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.used */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,2), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.active */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,3), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.inactive */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,4), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.active_anon */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,5), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.inactive_anon */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,6), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.active_file */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,7), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.inactive_file */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,8), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.highTotal */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,9), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.highFree */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,10), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.lowTotal */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,11), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.lowFree */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,12), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.unevictable */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,13), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.mlocked */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,14), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.dirty */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,15), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.writeback */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,16), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.filePages */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,17), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.mapped */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,18), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.anonpages */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,19), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.shmem */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,20), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.kernelStack */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,21), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.pageTables */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,22), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.NFS_Unstable */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,23), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.bounce */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,24), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.writebackTmp */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,25), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.slab */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,26), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.slabReclaimable */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,27), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.slabUnreclaimable */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,28), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* mem.numa.util.hugepagesTotal */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,29), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.util.hugepagesFree */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,30), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.util.hugepagesSurp */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,31), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.alloc.hit */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,32), PM_TYPE_U64, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.alloc.miss */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,33), PM_TYPE_U64, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.alloc.foreign */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,34), PM_TYPE_U64, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.alloc.interleave_hit */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,35), PM_TYPE_U64, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.alloc.local_node */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,36), PM_TYPE_U64, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* mem.numa.alloc.other_node */ + { NULL, + { PMDA_PMID(CLUSTER_NUMA_MEMINFO,37), PM_TYPE_U64, NODE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + +/* swap.length */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* swap.used */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* swap.free */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* hinv.physmem */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_MBYTE,0,0) }, }, + +/* mem.freemem */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* hinv.pagesize */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* mem.util.other */ + { NULL, + { PMDA_PMID(CLUSTER_MEMINFO,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* + * /proc/slabinfo cluster + */ + + /* mem.slabinfo.objects.active */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,0), PM_TYPE_U64, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* mem.slabinfo.objects.total */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,1), PM_TYPE_U64, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* mem.slabinfo.objects.size */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,2), PM_TYPE_U32, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + + /* mem.slabinfo.slabs.active */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,3), PM_TYPE_U32, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* mem.slabinfo.slabs.total */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,4), PM_TYPE_U32, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* mem.slabinfo.slabs.pages_per_slab */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,5), PM_TYPE_U32, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* mem.slabinfo.slabs.objects_per_slab */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,6), PM_TYPE_U32, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* mem.slabinfo.slabs.total_size */ + { NULL, + { PMDA_PMID(CLUSTER_SLAB,7), PM_TYPE_U64, SLAB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* + * /proc/loadavg cluster + */ + + /* kernel.all.load */ + { NULL, + { PMDA_PMID(CLUSTER_LOADAVG,0), PM_TYPE_FLOAT, LOADAVG_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* kernel.all.lastpid -- added by Mike Mason */ + { NULL, + { PMDA_PMID(CLUSTER_LOADAVG, 1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* kernel.all.runnable */ + { NULL, + { PMDA_PMID(CLUSTER_LOADAVG, 2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + + /* kernel.all.nprocs */ + { NULL, + { PMDA_PMID(CLUSTER_LOADAVG, 3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* + * /proc/net/dev cluster + */ + +/* network.interface.in.bytes */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,0), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* network.interface.in.packets */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,1), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.in.errors */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,2), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.in.drops */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,3), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.in.fifo */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,4), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.in.frame */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,5), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.in.compressed */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,6), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.in.mcasts */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,7), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.out.bytes */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,8), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* network.interface.out.packets */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,9), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.out.errors */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,10), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.out.drops */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,11), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.out.fifo */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,12), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.collisions */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,13), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.out.carrier */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,14), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.out.compressed */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,15), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.total.bytes */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,16), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,0,PM_SPACE_BYTE,0) }, }, + +/* network.interface.total.packets */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,17), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.total.errors */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,18), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.total.drops */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,19), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.total.mcasts */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,20), PM_TYPE_U64, NET_DEV_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* network.interface.mtu */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,21), PM_TYPE_U32, NET_DEV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,0,PM_SPACE_BYTE,0) }, }, + +/* network.interface.speed */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,22), PM_TYPE_FLOAT, NET_DEV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,-1,0,PM_SPACE_MBYTE,PM_TIME_SEC,0) }, }, + +/* network.interface.baudrate */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,23), PM_TYPE_U32, NET_DEV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) }, }, + +/* network.interface.duplex */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,24), PM_TYPE_U32, NET_DEV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* network.interface.up */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,25), PM_TYPE_U32, NET_DEV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* network.interface.running */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,26), PM_TYPE_U32, NET_DEV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* hinv.ninterface */ + { NULL, + { PMDA_PMID(CLUSTER_NET_DEV,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* network.interface.inet_addr */ + { NULL, + { PMDA_PMID(CLUSTER_NET_ADDR,0), PM_TYPE_STRING, NET_ADDR_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* network.interface.ipv6_addr */ + { NULL, + { PMDA_PMID(CLUSTER_NET_ADDR,1), PM_TYPE_STRING, NET_ADDR_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* network.interface.ipv6_scope */ + { NULL, + { PMDA_PMID(CLUSTER_NET_ADDR,2), PM_TYPE_STRING, NET_ADDR_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* network.interface.hw_addr */ + { NULL, + { PMDA_PMID(CLUSTER_NET_ADDR,3), PM_TYPE_STRING, NET_ADDR_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* + * filesys cluster + */ + +/* hinv.nmounts */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* filesys.capacity */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,1), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* filesys.used */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,2), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* filesys.free */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,3), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* filesys.maxfiles */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,4), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* filesys.usedfiles */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,5), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* filesys.freefiles */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,6), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* filesys.mountdir */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,7), PM_TYPE_STRING, FILESYS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* filesys.full */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,8), PM_TYPE_DOUBLE, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* filesys.blocksize */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,9), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + +/* filesys.avail */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,10), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* filesys.readonly */ + { NULL, + { PMDA_PMID(CLUSTER_FILESYS,11), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* + * tmpfs filesystem cluster + */ + +/* tmpfs.capacity */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,1), PM_TYPE_U64, TMPFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* tmpfs.used */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,2), PM_TYPE_U64, TMPFS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* tmpfs.free */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,3), PM_TYPE_U64, TMPFS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* tmpfs.maxfiles */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,4), PM_TYPE_U32, TMPFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* tmpfs.usedfiles */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,5), PM_TYPE_U32, TMPFS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* tmpfs.freefiles */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,6), PM_TYPE_U32, TMPFS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* tmpfs.full */ + { NULL, + { PMDA_PMID(CLUSTER_TMPFS,7), PM_TYPE_DOUBLE, TMPFS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* + * swapdev cluster + */ + +/* swapdev.free */ + { NULL, + { PMDA_PMID(CLUSTER_SWAPDEV,0), PM_TYPE_U32, SWAPDEV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* swapdev.length */ + { NULL, + { PMDA_PMID(CLUSTER_SWAPDEV,1), PM_TYPE_U32, SWAPDEV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* swapdev.maxswap */ + { NULL, + { PMDA_PMID(CLUSTER_SWAPDEV,2), PM_TYPE_U32, SWAPDEV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* swapdev.vlength */ + { NULL, + { PMDA_PMID(CLUSTER_SWAPDEV,3), PM_TYPE_U32, SWAPDEV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* swapdev.priority */ + { NULL, + { PMDA_PMID(CLUSTER_SWAPDEV,4), PM_TYPE_32, SWAPDEV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* + * socket stat cluster + */ + +/* network.sockstat.tcp.inuse */ + { &proc_net_sockstat.tcp[_PM_SOCKSTAT_INUSE], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.sockstat.tcp.highest */ + { &proc_net_sockstat.tcp[_PM_SOCKSTAT_HIGHEST], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.sockstat.tcp.util */ + { &proc_net_sockstat.tcp[_PM_SOCKSTAT_UTIL], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* network.sockstat.udp.inuse */ + { &proc_net_sockstat.udp[_PM_SOCKSTAT_INUSE], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.sockstat.udp.highest */ + { &proc_net_sockstat.udp[_PM_SOCKSTAT_HIGHEST], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.sockstat.udp.util */ + { &proc_net_sockstat.udp[_PM_SOCKSTAT_UTIL], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* network.sockstat.raw.inuse */ + { &proc_net_sockstat.raw[_PM_SOCKSTAT_INUSE], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,6), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.sockstat.raw.highest */ + { &proc_net_sockstat.raw[_PM_SOCKSTAT_HIGHEST], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,7), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.sockstat.raw.util */ + { &proc_net_sockstat.raw[_PM_SOCKSTAT_UTIL], + { PMDA_PMID(CLUSTER_NET_SOCKSTAT,8), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* + * nfs cluster + */ + +/* nfs.client.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs.client.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,4), PM_TYPE_U32, NFS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs.server.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,50), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs.server.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,12), PM_TYPE_U32, NFS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs3.client.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,60), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs3.client.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,61), PM_TYPE_U32, NFS3_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs3.server.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,62), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs3.server.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,63), PM_TYPE_U32, NFS3_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs4.client.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,64), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs4.client.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,65), PM_TYPE_U32, NFS4_CLI_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs4.server.calls */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,66), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* nfs4.server.reqs */ + { NULL, + { PMDA_PMID(CLUSTER_NET_NFS,67), PM_TYPE_U32, NFS4_SVR_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.rpccnt */ + { &proc_net_rpc.client.rpccnt, + { PMDA_PMID(CLUSTER_NET_NFS,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.rpcretrans */ + { &proc_net_rpc.client.rpcretrans, + { PMDA_PMID(CLUSTER_NET_NFS,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.rpcauthrefresh */ + { &proc_net_rpc.client.rpcauthrefresh, + { PMDA_PMID(CLUSTER_NET_NFS,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.netcnt */ + { &proc_net_rpc.client.netcnt, + { PMDA_PMID(CLUSTER_NET_NFS,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.netudpcnt */ + { &proc_net_rpc.client.netudpcnt, + { PMDA_PMID(CLUSTER_NET_NFS,25), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.nettcpcnt */ + { &proc_net_rpc.client.nettcpcnt, + { PMDA_PMID(CLUSTER_NET_NFS,26), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.client.nettcpconn */ + { &proc_net_rpc.client.nettcpconn, + { PMDA_PMID(CLUSTER_NET_NFS,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rpccnt */ + { &proc_net_rpc.server.rpccnt, + { PMDA_PMID(CLUSTER_NET_NFS,30), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rpcerr */ + { &proc_net_rpc.server.rpcerr, + { PMDA_PMID(CLUSTER_NET_NFS,31), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rpcbadfmt */ + { &proc_net_rpc.server.rpcbadfmt, + { PMDA_PMID(CLUSTER_NET_NFS,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rpcbadauth */ + { &proc_net_rpc.server.rpcbadauth, + { PMDA_PMID(CLUSTER_NET_NFS,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rpcbadclnt */ + { &proc_net_rpc.server.rpcbadclnt, + { PMDA_PMID(CLUSTER_NET_NFS,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rchits */ + { &proc_net_rpc.server.rchits, + { PMDA_PMID(CLUSTER_NET_NFS,35), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rcmisses */ + { &proc_net_rpc.server.rcmisses, + { PMDA_PMID(CLUSTER_NET_NFS,36), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.rcnocache */ + { &proc_net_rpc.server.rcnocache, + { PMDA_PMID(CLUSTER_NET_NFS,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_cached */ + { &proc_net_rpc.server.fh_cached, + { PMDA_PMID(CLUSTER_NET_NFS,38), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_valid */ + { &proc_net_rpc.server.fh_valid, + { PMDA_PMID(CLUSTER_NET_NFS,39), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_fixup */ + { &proc_net_rpc.server.fh_fixup, + { PMDA_PMID(CLUSTER_NET_NFS,40), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_lookup */ + { &proc_net_rpc.server.fh_lookup, + { PMDA_PMID(CLUSTER_NET_NFS,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_stale */ + { &proc_net_rpc.server.fh_stale, + { PMDA_PMID(CLUSTER_NET_NFS,42), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_concurrent */ + { &proc_net_rpc.server.fh_concurrent, + { PMDA_PMID(CLUSTER_NET_NFS,43), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.netcnt */ + { &proc_net_rpc.server.netcnt, + { PMDA_PMID(CLUSTER_NET_NFS,44), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.netudpcnt */ + { &proc_net_rpc.server.netudpcnt, + { PMDA_PMID(CLUSTER_NET_NFS,45), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.nettcpcnt */ + { &proc_net_rpc.server.nettcpcnt, + { PMDA_PMID(CLUSTER_NET_NFS,46), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.nettcpconn */ + { &proc_net_rpc.server.nettcpcnt, + { PMDA_PMID(CLUSTER_NET_NFS,47), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_anon */ + { &proc_net_rpc.server.fh_anon, + { PMDA_PMID(CLUSTER_NET_NFS,51), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_nocache_dir */ + { &proc_net_rpc.server.fh_nocache_dir, + { PMDA_PMID(CLUSTER_NET_NFS,52), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.fh_nocache_nondir */ + { &proc_net_rpc.server.fh_nocache_nondir, + { PMDA_PMID(CLUSTER_NET_NFS,53), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.io_read */ + { &proc_net_rpc.server.io_read, + { PMDA_PMID(CLUSTER_NET_NFS,54), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + +/* rpc.server.io_write */ + { &proc_net_rpc.server.io_write, + { PMDA_PMID(CLUSTER_NET_NFS,55), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + +/* rpc.server.th_cnt */ + { &proc_net_rpc.server.th_cnt, + { PMDA_PMID(CLUSTER_NET_NFS,56), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* rpc.server.th_fullcnt */ + { &proc_net_rpc.server.th_fullcnt, + { PMDA_PMID(CLUSTER_NET_NFS,57), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* + * /proc/partitions cluster + */ + +/* disk.partitions.read */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,0), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.partitions.write */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,1), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.partitions.total */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,2), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.partitions.blkread */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,3), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.partitions.blkwrite */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,4), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.partitions.blktotal */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,5), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* disk.partitions.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,6), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.partitions.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,7), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.partitions.total_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_PARTITIONS,8), PM_TYPE_U32, PARTITIONS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + +/* disk.dev.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,38), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.dev.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,39), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.dev.total_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,40), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.all.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.all.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,42), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* disk.all.total_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_STAT,43), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + +/* + * kernel_uname cluster + */ + +/* kernel.uname.release */ + { kernel_uname.release, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* kernel.uname.version */ + { kernel_uname.version, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* kernel.uname.sysname */ + { kernel_uname.sysname, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* kernel.uname.machine */ + { kernel_uname.machine, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 3), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* kernel.uname.nodename */ + { kernel_uname.nodename, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 4), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* pmda.uname */ + { NULL, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 5), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* pmda.version */ + { NULL, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 6), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* kernel.uname.distro */ + { NULL, + { PMDA_PMID(CLUSTER_KERNEL_UNAME, 7), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* + * network snmp cluster + */ + +/* network.ip.forwarding */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FORWARDING], + { PMDA_PMID(CLUSTER_NET_SNMP,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.defaultttl */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_DEFAULTTTL], + { PMDA_PMID(CLUSTER_NET_SNMP,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inreceives */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INRECEIVES], + { PMDA_PMID(CLUSTER_NET_SNMP,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inhdrerrors */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INHDRERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inaddrerrors */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INADDRERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.forwdatagrams */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FORWDATAGRAMS], + { PMDA_PMID(CLUSTER_NET_SNMP,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inunknownprotos */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INUNKNOWNPROTOS], + { PMDA_PMID(CLUSTER_NET_SNMP,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.indiscards */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INDISCARDS], + { PMDA_PMID(CLUSTER_NET_SNMP,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.indelivers */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INDELIVERS], + { PMDA_PMID(CLUSTER_NET_SNMP,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outrequests */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTREQUESTS], + { PMDA_PMID(CLUSTER_NET_SNMP,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outdiscards */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTDISCARDS], + { PMDA_PMID(CLUSTER_NET_SNMP,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outnoroutes */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTNOROUTES], + { PMDA_PMID(CLUSTER_NET_SNMP,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.reasmtimeout */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMTIMEOUT], + { PMDA_PMID(CLUSTER_NET_SNMP,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.reasmreqds */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMREQDS], + { PMDA_PMID(CLUSTER_NET_SNMP,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.reasmoks */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMOKS], + { PMDA_PMID(CLUSTER_NET_SNMP,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.reasmfails */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMFAILS], + { PMDA_PMID(CLUSTER_NET_SNMP,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.fragoks */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGOKS], + { PMDA_PMID(CLUSTER_NET_SNMP,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.fragfails */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGFAILS], + { PMDA_PMID(CLUSTER_NET_SNMP,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.fragcreates */ + { &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGCREATES], + { PMDA_PMID(CLUSTER_NET_SNMP,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + + +/* network.icmp.inmsgs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INMSGS], + { PMDA_PMID(CLUSTER_NET_SNMP,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inerrors */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,21), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.indestunreachs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INDESTUNREACHS], + { PMDA_PMID(CLUSTER_NET_SNMP,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.intimeexcds */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMEEXCDS], + { PMDA_PMID(CLUSTER_NET_SNMP,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inparmprobs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INPARMPROBS], + { PMDA_PMID(CLUSTER_NET_SNMP,24), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.insrcquenchs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INSRCQUENCHS], + { PMDA_PMID(CLUSTER_NET_SNMP,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inredirects */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INREDIRECTS], + { PMDA_PMID(CLUSTER_NET_SNMP,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inechos */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INECHOS], + { PMDA_PMID(CLUSTER_NET_SNMP,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inechoreps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INECHOREPS], + { PMDA_PMID(CLUSTER_NET_SNMP,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.intimestamps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMESTAMPS], + { PMDA_PMID(CLUSTER_NET_SNMP,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.intimestampreps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMESTAMPREPS], + { PMDA_PMID(CLUSTER_NET_SNMP,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inaddrmasks */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INADDRMASKS], + { PMDA_PMID(CLUSTER_NET_SNMP,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.inaddrmaskreps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INADDRMASKREPS], + { PMDA_PMID(CLUSTER_NET_SNMP,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outmsgs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTMSGS], + { PMDA_PMID(CLUSTER_NET_SNMP,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outerrors */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outdestunreachs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTDESTUNREACHS], + { PMDA_PMID(CLUSTER_NET_SNMP,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outtimeexcds */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMEEXCDS], + { PMDA_PMID(CLUSTER_NET_SNMP,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outparmprobs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTPARMPROBS], + { PMDA_PMID(CLUSTER_NET_SNMP,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outsrcquenchs */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTSRCQUENCHS], + { PMDA_PMID(CLUSTER_NET_SNMP,38), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outredirects */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTREDIRECTS], + { PMDA_PMID(CLUSTER_NET_SNMP,39), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outechos */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTECHOS], + { PMDA_PMID(CLUSTER_NET_SNMP,40), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outechoreps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTECHOREPS], + { PMDA_PMID(CLUSTER_NET_SNMP,41), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outtimestamps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMESTAMPS], + { PMDA_PMID(CLUSTER_NET_SNMP,42), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outtimestampreps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMESTAMPREPS], + { PMDA_PMID(CLUSTER_NET_SNMP,43), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outaddrmasks */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTADDRMASKS], + { PMDA_PMID(CLUSTER_NET_SNMP,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.outaddrmaskreps */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTADDRMASKREPS], + { PMDA_PMID(CLUSTER_NET_SNMP,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmp.incsumerrors */ + { &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INCSUMERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + + +/* network.tcp.rtoalgorithm */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOALGORITHM], + { PMDA_PMID(CLUSTER_NET_SNMP,50), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.rtomin */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOMIN], + { PMDA_PMID(CLUSTER_NET_SNMP,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.rtomax */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOMAX], + { PMDA_PMID(CLUSTER_NET_SNMP,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.maxconn */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_MAXCONN], + { PMDA_PMID(CLUSTER_NET_SNMP,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.established */ + { &proc_net_tcp.stat[_PM_TCP_ESTABLISHED], + { PMDA_PMID(CLUSTER_NET_TCP, 1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.syn_sent */ + { &proc_net_tcp.stat[_PM_TCP_SYN_SENT], + { PMDA_PMID(CLUSTER_NET_TCP, 2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.syn_recv */ + { &proc_net_tcp.stat[_PM_TCP_SYN_RECV], + { PMDA_PMID(CLUSTER_NET_TCP, 3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.fin_wait1 */ + { &proc_net_tcp.stat[_PM_TCP_FIN_WAIT1], + { PMDA_PMID(CLUSTER_NET_TCP, 4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.fin_wait2 */ + { &proc_net_tcp.stat[_PM_TCP_FIN_WAIT2], + { PMDA_PMID(CLUSTER_NET_TCP, 5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.time_wait */ + { &proc_net_tcp.stat[_PM_TCP_TIME_WAIT], + { PMDA_PMID(CLUSTER_NET_TCP, 6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.close */ + { &proc_net_tcp.stat[_PM_TCP_CLOSE], + { PMDA_PMID(CLUSTER_NET_TCP, 7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.close_wait */ + { &proc_net_tcp.stat[_PM_TCP_CLOSE_WAIT], + { PMDA_PMID(CLUSTER_NET_TCP, 8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.last_ack */ + { &proc_net_tcp.stat[_PM_TCP_LAST_ACK], + { PMDA_PMID(CLUSTER_NET_TCP, 9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.listen */ + { &proc_net_tcp.stat[_PM_TCP_LISTEN], + { PMDA_PMID(CLUSTER_NET_TCP, 10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcpconn.closing */ + { &proc_net_tcp.stat[_PM_TCP_CLOSING], + { PMDA_PMID(CLUSTER_NET_TCP, 11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.activeopens */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ACTIVEOPENS], + { PMDA_PMID(CLUSTER_NET_SNMP,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.passiveopens */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_PASSIVEOPENS], + { PMDA_PMID(CLUSTER_NET_SNMP,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.attemptfails */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ATTEMPTFAILS], + { PMDA_PMID(CLUSTER_NET_SNMP,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.estabresets */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ESTABRESETS], + { PMDA_PMID(CLUSTER_NET_SNMP,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.currestab */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_CURRESTAB], + { PMDA_PMID(CLUSTER_NET_SNMP,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.insegs */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INSEGS], + { PMDA_PMID(CLUSTER_NET_SNMP,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.outsegs */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_OUTSEGS], + { PMDA_PMID(CLUSTER_NET_SNMP,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.retranssegs */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RETRANSSEGS], + { PMDA_PMID(CLUSTER_NET_SNMP,61), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.inerrs */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INERRS], + { PMDA_PMID(CLUSTER_NET_SNMP,62), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.outrsts */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_OUTRSTS], + { PMDA_PMID(CLUSTER_NET_SNMP,63), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.incsumerrors */ + { &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INCSUMERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,64), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.indatagrams */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INDATAGRAMS], + { PMDA_PMID(CLUSTER_NET_SNMP,70), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.noports */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_NOPORTS], + { PMDA_PMID(CLUSTER_NET_SNMP,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.inerrors */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.outdatagrams */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_OUTDATAGRAMS], + { PMDA_PMID(CLUSTER_NET_SNMP,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.recvbuferrors */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_RECVBUFERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.sndbuferrors */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_SNDBUFERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udp.incsumerrors */ + { &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INCSUMERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,83), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.indatagrams */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INDATAGRAMS], + { PMDA_PMID(CLUSTER_NET_SNMP,77), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.noports */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_NOPORTS], + { PMDA_PMID(CLUSTER_NET_SNMP,78), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.inerrors */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,79), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.outdatagrams */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_OUTDATAGRAMS], + { PMDA_PMID(CLUSTER_NET_SNMP,80), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.recvbuferrors */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_RECVBUFERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,81), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.sndbuferrors */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_SNDBUFERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,82), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.udplite.incsumerrors */ + { &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INCSUMERRORS], + { PMDA_PMID(CLUSTER_NET_SNMP,84), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmpmsg.intype */ + { &_pm_proc_net_snmp.icmpmsg[_PM_SNMP_ICMPMSG_INTYPE], + { PMDA_PMID(CLUSTER_NET_SNMP,88), PM_TYPE_U64, ICMPMSG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.icmpmsg.outtype */ + { &_pm_proc_net_snmp.icmpmsg[_PM_SNMP_ICMPMSG_OUTTYPE], + { PMDA_PMID(CLUSTER_NET_SNMP,89), PM_TYPE_U64, ICMPMSG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* + * network netstat cluster + */ + +/* network.ip.innoroutes */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INNOROUTES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.intruncatedpkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INTRUNCATEDPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inmcastpkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INMCASTPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outmcastpkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTMCASTPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inbcastpkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INBCASTPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outbcastpkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTBCASTPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inoctets */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INOCTETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outoctets */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTOCTETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inmcastoctets */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INMCASTOCTETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outmcastoctets */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTMCASTOCTETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.inbcastoctets */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INBCASTOCTETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.outbcastoctets */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTBCASTOCTETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.csumerrors */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_CSUMERRORS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.noectpkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_NOECTPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.ect1pkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_ECT1PKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.ect0pkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_ECT0PKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.ip.cepkts */ + { &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_CEPKTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.syncookiessent */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESSENT], + { PMDA_PMID(CLUSTER_NET_NETSTAT,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.syncookiesrecv */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESRECV], + { PMDA_PMID(CLUSTER_NET_NETSTAT,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.syncookiesfailed */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESFAILED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,19), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.embryonicrsts */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_EMBRYONICRSTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.prunecalled */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PRUNECALLED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,21), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.rcvpruned */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_RCVPRUNED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.ofopruned */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_OFOPRUNED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.outofwindowicmps */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_OUTOFWINDOWICMPS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,24), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.lockdroppedicmps */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LOCKDROPPEDICMPS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.arpfilter */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_ARPFILTER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.timewaited */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.timewaitrecycled */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITRECYCLED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.timewaitkilled */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITKILLED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.pawspassiverejected */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSPASSIVEREJECTED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.pawsactiverejected */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSACTIVEREJECTED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.pawsestabrejected */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSESTABREJECTED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.delayedacks */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.delayedacklocked */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKLOCKED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.delayedacklost */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKLOST], + { PMDA_PMID(CLUSTER_NET_NETSTAT,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.listenoverflows */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LISTENOVERFLOWS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.listendrops */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LISTENDROPS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.prequeued */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPREQUEUED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,38), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.directcopyfrombacklog */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMBACKLOG], + { PMDA_PMID(CLUSTER_NET_NETSTAT,39), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.directcopyfromprequeue */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMPREQUEUE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,40), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.prequeuedropped */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPREQUEUEDROPPED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,41), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.hphits*/ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPHITS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,42), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.hphitstouser */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPHITSTOUSER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,43), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.pureacks */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPUREACKS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.hpacks */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPACKS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.renorecovery */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENORECOVERY], + { PMDA_PMID(CLUSTER_NET_NETSTAT,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackrecovery */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRECOVERY], + { PMDA_PMID(CLUSTER_NET_NETSTAT,47), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackreneging */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRENEGING], + { PMDA_PMID(CLUSTER_NET_NETSTAT,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fackreorder */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFACKREORDER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,49), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackreorder */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKREORDER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,50), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.renoreorder */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENOREORDER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.tsreorder */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTSREORDER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fullundo */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFULLUNDO], + { PMDA_PMID(CLUSTER_NET_NETSTAT,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.partialundo */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPARTIALUNDO], + { PMDA_PMID(CLUSTER_NET_NETSTAT,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackundo */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKUNDO], + { PMDA_PMID(CLUSTER_NET_NETSTAT,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.lossundo */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSUNDO], + { PMDA_PMID(CLUSTER_NET_NETSTAT,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.lostretransmit */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSTRETRANSMIT], + { PMDA_PMID(CLUSTER_NET_NETSTAT,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.renofailures */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENOFAILURES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackfailures */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKFAILURES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.lossfailures */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSFAILURES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastretrans */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTRETRANS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,61), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.forwardretrans */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFORWARDRETRANS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,62), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.slowstartretrans */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSLOWSTARTRETRANS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,63), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.timeouts */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTIMEOUTS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,64), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.lossprobes */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSPROBES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,65), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.lossproberecovery */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSPROBERECOVERY], + { PMDA_PMID(CLUSTER_NET_NETSTAT,66), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.renorecoveryfail */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENORECOVERYFAIL], + { PMDA_PMID(CLUSTER_NET_NETSTAT,67), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackrecoveryfail */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRECOVERYFAIL], + { PMDA_PMID(CLUSTER_NET_NETSTAT,68), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.schedulerfailed */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSCHEDULERFAILED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,69), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.rcvcollapsed */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRCVCOLLAPSED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,70), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackoldsent */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOLDSENT], + { PMDA_PMID(CLUSTER_NET_NETSTAT,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackofosent */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOFOSENT], + { PMDA_PMID(CLUSTER_NET_NETSTAT,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackrecv */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKRECV], + { PMDA_PMID(CLUSTER_NET_NETSTAT,73), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackoforecv */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOFORECV], + { PMDA_PMID(CLUSTER_NET_NETSTAT,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.abortondata */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONDATA], + { PMDA_PMID(CLUSTER_NET_NETSTAT,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.abortonclose */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONCLOSE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.abortonmemory */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONMEMORY], + { PMDA_PMID(CLUSTER_NET_NETSTAT,77), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.abortontimeout */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONTIMEOUT], + { PMDA_PMID(CLUSTER_NET_NETSTAT,78), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.abortonlinger */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONLINGER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,79), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.abortfailed */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTFAILED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,80), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.memorypressures */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMEMORYPRESSURES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,81), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackdiscard */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKDISCARD], + { PMDA_PMID(CLUSTER_NET_NETSTAT,82), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackignoredold */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDOLD], + { PMDA_PMID(CLUSTER_NET_NETSTAT,83), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.dsackignorednoundo */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDNOUNDO], + { PMDA_PMID(CLUSTER_NET_NETSTAT,84), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.spuriousrtos */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSPURIOUSRTOS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,85), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.md5notfound */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMD5NOTFOUND], + { PMDA_PMID(CLUSTER_NET_NETSTAT,86), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.md5unexpected */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMD5UNEXPECTED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,87), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackshifted */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKSHIFTED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,88), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackmerged */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKMERGED], + { PMDA_PMID(CLUSTER_NET_NETSTAT,89), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.sackshiftfallback */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKSHIFTFALLBACK], + { PMDA_PMID(CLUSTER_NET_NETSTAT,90), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.backlogdrop */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPBACKLOGDROP], + { PMDA_PMID(CLUSTER_NET_NETSTAT,91), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.minttldrop */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMINTTLDROP], + { PMDA_PMID(CLUSTER_NET_NETSTAT,92), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.deferacceptdrop */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDEFERACCEPTDROP], + { PMDA_PMID(CLUSTER_NET_NETSTAT,93), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.iprpfilter */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_IPRPFILTER], + { PMDA_PMID(CLUSTER_NET_NETSTAT,94), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.timewaitoverflow */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTIMEWAITOVERFLOW], + { PMDA_PMID(CLUSTER_NET_NETSTAT,95), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.reqqfulldocookies */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPREQQFULLDOCOOKIES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,96), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.reqqfulldrop */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPREQQFULLDROP], + { PMDA_PMID(CLUSTER_NET_NETSTAT,97), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.retransfail */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRETRANSFAIL], + { PMDA_PMID(CLUSTER_NET_NETSTAT,98), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.rcvcoalesce */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRCVCOALESCE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,99), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.ofoqueue */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFOQUEUE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,100), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.ofodrop */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFODROP], + { PMDA_PMID(CLUSTER_NET_NETSTAT,101), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.ofomerge */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFOMERGE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,102), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.challengeack */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPCHALLENGEACK], + { PMDA_PMID(CLUSTER_NET_NETSTAT,103), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.synchallenge */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSYNCHALLENGE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,104), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastopenactive */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,105), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastopenactivefail */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVEFAIL], + { PMDA_PMID(CLUSTER_NET_NETSTAT,106), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastopenpassive */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENPASSIVE], + { PMDA_PMID(CLUSTER_NET_NETSTAT,107), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastopenpassivefail */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENPASSIVEFAIL], + { PMDA_PMID(CLUSTER_NET_NETSTAT,108), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastopenlistenoverflow */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENLISTENOVERFLOW], + { PMDA_PMID(CLUSTER_NET_NETSTAT,109), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fastopencookiereqd */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENCOOKIEREQD], + { PMDA_PMID(CLUSTER_NET_NETSTAT,110), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.spuriousrtxhostqueues */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSPURIOUS_RTX_HOSTQUEUES], + { PMDA_PMID(CLUSTER_NET_NETSTAT,111), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.busypollrxpackets */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_BUSYPOLLRXPACKETS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,112), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.autocorking */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPAUTOCORKING], + { PMDA_PMID(CLUSTER_NET_NETSTAT,113), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.fromzerowindowadv */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFROMZEROWINDOWADV], + { PMDA_PMID(CLUSTER_NET_NETSTAT,114), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.tozerowindowadv */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTOZEROWINDOWADV], + { PMDA_PMID(CLUSTER_NET_NETSTAT,115), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.wantzerowindowadv */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPWANTZEROWINDOWADV], + { PMDA_PMID(CLUSTER_NET_NETSTAT,116), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.synretrans */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSYNRETRANS], + { PMDA_PMID(CLUSTER_NET_NETSTAT,117), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* network.tcp.origdatasent */ + { &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPORIGDATASENT], + { PMDA_PMID(CLUSTER_NET_NETSTAT,118), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* hinv.map.scsi */ + { NULL, + { PMDA_PMID(CLUSTER_SCSI,0), PM_TYPE_STRING, SCSI_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* hinv.map.lvname */ + { NULL, + { PMDA_PMID(CLUSTER_LV,0), PM_TYPE_STRING, LV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* hinv.nlv */ + { NULL, + { PMDA_PMID(CLUSTER_LV,1), PM_TYPE_U32, LV_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* + * /proc/cpuinfo cluster (cpu indom) + */ + +/* hinv.cpu.clock */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 0), PM_TYPE_FLOAT, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,-6,0) } }, + +/* hinv.cpu.vendor */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 1), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.cpu.model */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 2), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.cpu.stepping */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 3), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.cpu.cache */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 4), PM_TYPE_U32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* hinv.cpu.bogomips */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 5), PM_TYPE_FLOAT, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.map.cpu_num */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 6), PM_TYPE_U32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.machine */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 7), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.map.cpu_node */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 8), PM_TYPE_U32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.cpu.model_name */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 9), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.cpu.flags */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 10), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* hinv.cpu.cache_alignment */ + { NULL, + { PMDA_PMID(CLUSTER_CPUINFO, 11), PM_TYPE_U32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,PM_SPACE_BYTE,0,0) } }, + +/* + * semaphore limits cluster + * Cluster added by Mike Mason + */ + +/* ipc.sem.max_semmap */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_semid */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_sem */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.num_undo */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_perid */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_ops */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_undoent */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.sz_semundo */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_semval */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.sem.max_exit */ + { NULL, + { PMDA_PMID(CLUSTER_SEM_LIMITS, 9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* + * message limits cluster + * Cluster added by Mike Mason + */ + +/* ipc.msg.sz_pool */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0, PM_SPACE_KBYTE,0,0)}}, + +/* ipc.msg.mapent */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.msg.max_msgsz */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.msg.max_defmsgq */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.msg.max_msgqid */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.msg.max_msgseg */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)}}, + +/* ipc.msg.max_smsghdr */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.msg.max_seg */ + { NULL, + { PMDA_PMID(CLUSTER_MSG_LIMITS, 7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* + * shared memory limits cluster + * Cluster added by Mike Mason + */ + +/* ipc.shm.max_segsz */ + { NULL, + { PMDA_PMID(CLUSTER_SHM_LIMITS, 0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, + +/* ipc.shm.min_segsz */ + { NULL, + { PMDA_PMID(CLUSTER_SHM_LIMITS, 1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, + +/* ipc.shm.max_seg */ + { NULL, + { PMDA_PMID(CLUSTER_SHM_LIMITS, 2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.shm.max_segproc */ + { NULL, + { PMDA_PMID(CLUSTER_SHM_LIMITS, 3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* ipc.shm.max_shmsys */ + { NULL, + { PMDA_PMID(CLUSTER_SHM_LIMITS, 4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* + * number of users cluster + */ + +/* kernel.all.nusers */ + { NULL, + { PMDA_PMID(CLUSTER_NUSERS, 0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* + * /proc/sys/fs vfs cluster + */ + +/* vfs.files */ + { &proc_sys_fs.fs_files_count, + { PMDA_PMID(CLUSTER_VFS,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { &proc_sys_fs.fs_files_free, + { PMDA_PMID(CLUSTER_VFS,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { &proc_sys_fs.fs_files_max, + { PMDA_PMID(CLUSTER_VFS,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { &proc_sys_fs.fs_inodes_count, + { PMDA_PMID(CLUSTER_VFS,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { &proc_sys_fs.fs_inodes_free, + { PMDA_PMID(CLUSTER_VFS,4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { &proc_sys_fs.fs_dentry_count, + { PMDA_PMID(CLUSTER_VFS,5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { &proc_sys_fs.fs_dentry_free, + { PMDA_PMID(CLUSTER_VFS,6), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* + * mem.vmstat cluster + */ + + /* mem.vmstat.nr_dirty */ + { &_pm_proc_vmstat.nr_dirty, + {PMDA_PMID(28,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_writeback */ + { &_pm_proc_vmstat.nr_writeback, + {PMDA_PMID(28,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_unstable */ + { &_pm_proc_vmstat.nr_unstable, + {PMDA_PMID(28,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_page_table_pages */ + { &_pm_proc_vmstat.nr_page_table_pages, + {PMDA_PMID(28,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_mapped */ + { &_pm_proc_vmstat.nr_mapped, + {PMDA_PMID(28,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_slab */ + { &_pm_proc_vmstat.nr_slab, + {PMDA_PMID(28,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.pgpgin */ + { &_pm_proc_vmstat.pgpgin, + {PMDA_PMID(28,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgpgout */ + { &_pm_proc_vmstat.pgpgout, + {PMDA_PMID(28,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pswpin */ + { &_pm_proc_vmstat.pswpin, + {PMDA_PMID(28,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pswpout */ + { &_pm_proc_vmstat.pswpout, + {PMDA_PMID(28,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgalloc_high */ + { &_pm_proc_vmstat.pgalloc_high, + {PMDA_PMID(28,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgalloc_normal */ + { &_pm_proc_vmstat.pgalloc_normal, + {PMDA_PMID(28,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgalloc_dma */ + { &_pm_proc_vmstat.pgalloc_dma, + {PMDA_PMID(28,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgfree */ + { &_pm_proc_vmstat.pgfree, + {PMDA_PMID(28,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgactivate */ + { &_pm_proc_vmstat.pgactivate, + {PMDA_PMID(28,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgdeactivate */ + { &_pm_proc_vmstat.pgdeactivate, + {PMDA_PMID(28,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgfault */ + { &_pm_proc_vmstat.pgfault, + {PMDA_PMID(28,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgmajfault */ + { &_pm_proc_vmstat.pgmajfault, + {PMDA_PMID(28,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgrefill_high */ + { &_pm_proc_vmstat.pgrefill_high, + {PMDA_PMID(28,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgrefill_normal */ + { &_pm_proc_vmstat.pgrefill_normal, + {PMDA_PMID(28,19), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgrefill_dma */ + { &_pm_proc_vmstat.pgrefill_dma, + {PMDA_PMID(28,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgsteal_high */ + { &_pm_proc_vmstat.pgsteal_high, + {PMDA_PMID(28,21), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgsteal_normal */ + { &_pm_proc_vmstat.pgsteal_normal, + {PMDA_PMID(28,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgsteal_dma */ + { &_pm_proc_vmstat.pgsteal_dma, + {PMDA_PMID(28,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_kswapd_high */ + { &_pm_proc_vmstat.pgscan_kswapd_high, + {PMDA_PMID(28,24), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_kswapd_normal */ + { &_pm_proc_vmstat.pgscan_kswapd_normal, + {PMDA_PMID(28,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_kswapd_dma */ + { &_pm_proc_vmstat.pgscan_kswapd_dma, + {PMDA_PMID(28,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_direct_high */ + { &_pm_proc_vmstat.pgscan_direct_high, + {PMDA_PMID(28,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_direct_normal */ + { &_pm_proc_vmstat.pgscan_direct_normal, + {PMDA_PMID(28,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_direct_dma */ + { &_pm_proc_vmstat.pgscan_direct_dma, + {PMDA_PMID(28,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pginodesteal */ + { &_pm_proc_vmstat.pginodesteal, + {PMDA_PMID(28,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.slabs_scanned */ + { &_pm_proc_vmstat.slabs_scanned, + {PMDA_PMID(28,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.kswapd_steal */ + { &_pm_proc_vmstat.kswapd_steal, + {PMDA_PMID(28,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.kswapd_inodesteal */ + { &_pm_proc_vmstat.kswapd_inodesteal, + {PMDA_PMID(28,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pageoutrun */ + { &_pm_proc_vmstat.pageoutrun, + {PMDA_PMID(28,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.allocstall */ + { &_pm_proc_vmstat.allocstall, + {PMDA_PMID(28,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgrotated */ + { &_pm_proc_vmstat.pgrotated, + {PMDA_PMID(28,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_slab_reclaimable */ + { &_pm_proc_vmstat.nr_slab_reclaimable, + {PMDA_PMID(28,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_slab_unreclaimable */ + { &_pm_proc_vmstat.nr_slab_unreclaimable, + {PMDA_PMID(28,38), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_anon_pages */ + { &_pm_proc_vmstat.nr_anon_pages, + {PMDA_PMID(28,39), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_bounce */ + { &_pm_proc_vmstat.nr_bounce, + {PMDA_PMID(28,40), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_file_pages */ + { &_pm_proc_vmstat.nr_file_pages, + {PMDA_PMID(28,41), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* mem.vmstat.nr_vmscan_write */ + { &_pm_proc_vmstat.nr_vmscan_write, + {PMDA_PMID(28,42), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.htlb_buddy_alloc_fail */ + { &_pm_proc_vmstat.htlb_buddy_alloc_fail, + {PMDA_PMID(28,43), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.htlb_buddy_alloc_success */ + { &_pm_proc_vmstat.htlb_buddy_alloc_success, + {PMDA_PMID(28,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_active_anon */ + { &_pm_proc_vmstat.nr_active_anon, + {PMDA_PMID(28,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_active_file */ + { &_pm_proc_vmstat.nr_active_file, + {PMDA_PMID(28,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_free_pages */ + { &_pm_proc_vmstat.nr_free_pages, + {PMDA_PMID(28,47), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_inactive_anon */ + { &_pm_proc_vmstat.nr_inactive_anon, + {PMDA_PMID(28,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_inactive_file */ + { &_pm_proc_vmstat.nr_inactive_file, + {PMDA_PMID(28,49), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_isolated_anon */ + { &_pm_proc_vmstat.nr_isolated_anon, + {PMDA_PMID(28,50), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_isolated_file */ + { &_pm_proc_vmstat.nr_isolated_file, + {PMDA_PMID(28,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_kernel_stack */ + { &_pm_proc_vmstat.nr_kernel_stack, + {PMDA_PMID(28,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_mlock */ + { &_pm_proc_vmstat.nr_mlock, + {PMDA_PMID(28,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_shmem */ + { &_pm_proc_vmstat.nr_shmem, + {PMDA_PMID(28,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_unevictable */ + { &_pm_proc_vmstat.nr_unevictable, + {PMDA_PMID(28,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_writeback_temp */ + { &_pm_proc_vmstat.nr_writeback_temp, + {PMDA_PMID(28,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.compact_blocks_moved */ + { &_pm_proc_vmstat.compact_blocks_moved, + {PMDA_PMID(28,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.compact_fail */ + { &_pm_proc_vmstat.compact_fail, + {PMDA_PMID(28,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.compact_pagemigrate_failed */ + { &_pm_proc_vmstat.compact_pagemigrate_failed, + {PMDA_PMID(28,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.compact_pages_moved */ + { &_pm_proc_vmstat.compact_pages_moved, + {PMDA_PMID(28,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.compact_stall */ + { &_pm_proc_vmstat.compact_stall, + {PMDA_PMID(28,61), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.compact_success */ + { &_pm_proc_vmstat.compact_success, + {PMDA_PMID(28,62), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgalloc_dma32 */ + { &_pm_proc_vmstat.pgalloc_dma32, + {PMDA_PMID(28,63), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgalloc_movable */ + { &_pm_proc_vmstat.pgalloc_movable, + {PMDA_PMID(28,64), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgrefill_dma32 */ + { &_pm_proc_vmstat.pgrefill_dma32, + {PMDA_PMID(28,65), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgrefill_movable */ + { &_pm_proc_vmstat.pgrefill_movable, + {PMDA_PMID(28,66), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_direct_dma32 */ + { &_pm_proc_vmstat.pgscan_direct_dma32, + {PMDA_PMID(28,67), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_direct_movable */ + { &_pm_proc_vmstat.pgscan_direct_movable, + {PMDA_PMID(28,68), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_kswapd_dma32 */ + { &_pm_proc_vmstat.pgscan_kswapd_dma32, + {PMDA_PMID(28,69), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgscan_kswapd_movable */ + { &_pm_proc_vmstat.pgscan_kswapd_movable, + {PMDA_PMID(28,70), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgsteal_dma32 */ + { &_pm_proc_vmstat.pgsteal_dma32, + {PMDA_PMID(28,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.pgsteal_movable */ + { &_pm_proc_vmstat.pgsteal_movable, + {PMDA_PMID(28,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.thp_fault_alloc */ + { &_pm_proc_vmstat.thp_fault_alloc, + {PMDA_PMID(28,73), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.thp_fault_fallback */ + { &_pm_proc_vmstat.thp_fault_fallback, + {PMDA_PMID(28,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.thp_collapse_alloc */ + { &_pm_proc_vmstat.thp_collapse_alloc, + {PMDA_PMID(28,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.thp_collapse_alloc_failed */ + { &_pm_proc_vmstat.thp_collapse_alloc_failed, + {PMDA_PMID(28,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.thp_split */ + { &_pm_proc_vmstat.thp_split, + {PMDA_PMID(28,77), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_cleared */ + { &_pm_proc_vmstat.unevictable_pgs_cleared, + {PMDA_PMID(28,78), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_culled */ + { &_pm_proc_vmstat.unevictable_pgs_culled, + {PMDA_PMID(28,79), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_mlocked */ + { &_pm_proc_vmstat.unevictable_pgs_mlocked, + {PMDA_PMID(28,80), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_mlockfreed */ + { &_pm_proc_vmstat.unevictable_pgs_mlockfreed, + {PMDA_PMID(28,81), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_munlocked */ + { &_pm_proc_vmstat.unevictable_pgs_munlocked, + {PMDA_PMID(28,82), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_rescued */ + { &_pm_proc_vmstat.unevictable_pgs_rescued, + {PMDA_PMID(28,83), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_scanned */ + { &_pm_proc_vmstat.unevictable_pgs_scanned, + {PMDA_PMID(28,84), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.unevictable_pgs_stranded */ + { &_pm_proc_vmstat.unevictable_pgs_stranded, + {PMDA_PMID(28,85), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.zone_reclaim_failed */ + { &_pm_proc_vmstat.zone_reclaim_failed, + {PMDA_PMID(28,86), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.kswapd_low_wmark_hit_quickly */ + { &_pm_proc_vmstat.kswapd_low_wmark_hit_quickly, + {PMDA_PMID(28,87), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.kswapd_high_wmark_hit_quickly */ + { &_pm_proc_vmstat.kswapd_high_wmark_hit_quickly, + {PMDA_PMID(28,88), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.kswapd_skip_congestion_wait */ + { &_pm_proc_vmstat.kswapd_skip_congestion_wait, + {PMDA_PMID(28,89), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_anon_transparent_hugepages */ + { &_pm_proc_vmstat.nr_anon_transparent_hugepages, + {PMDA_PMID(28,90), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_dirtied */ + { &_pm_proc_vmstat.nr_dirtied, + {PMDA_PMID(28,91), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_dirty_background_threshold */ + { &_pm_proc_vmstat.nr_dirty_background_threshold, + {PMDA_PMID(28,92), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_dirty_threshold */ + { &_pm_proc_vmstat.nr_dirty_threshold, + {PMDA_PMID(28,93), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.nr_written */ + { &_pm_proc_vmstat.nr_written, + {PMDA_PMID(28,94), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.numa_foreign */ + { &_pm_proc_vmstat.numa_foreign, + {PMDA_PMID(28,95), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.numa_hit */ + { &_pm_proc_vmstat.numa_hit, + {PMDA_PMID(28,96), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.numa_interleave */ + { &_pm_proc_vmstat.numa_interleave, + {PMDA_PMID(28,97), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.numa_local */ + { &_pm_proc_vmstat.numa_local, + {PMDA_PMID(28,98), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.numa_miss */ + { &_pm_proc_vmstat.numa_miss, + {PMDA_PMID(28,99), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* mem.vmstat.numa_other */ + { &_pm_proc_vmstat.numa_other, + {PMDA_PMID(28,100), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* + * sysfs_kernel cluster + */ + /* sysfs.kernel.uevent_seqnum */ + { &sysfs_kernel.uevent_seqnum, + { PMDA_PMID(CLUSTER_SYSFS_KERNEL,0), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* + * /proc/interrupts cluster + */ + /* kernel.all.interrupts.errors */ + { &irq_err_count, + { PMDA_PMID(CLUSTER_INTERRUPTS, 3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* kernel.percpu.interrupts.line[] */ + { NULL, { PMDA_PMID(CLUSTER_INTERRUPT_LINES, 0), PM_TYPE_U32, + CPU_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* kernel.percpu.interrupts.[] */ + { NULL, { PMDA_PMID(CLUSTER_INTERRUPT_OTHER, 0), PM_TYPE_U32, + CPU_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* + * disk.dm cluster + */ + /* disk.dm.read */ + { NULL, + { PMDA_PMID(CLUSTER_DM,0), KERNEL_ULONG, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.write */ + { NULL, + { PMDA_PMID(CLUSTER_DM,1), KERNEL_ULONG, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.total */ + { NULL, + { PMDA_PMID(CLUSTER_DM,2), KERNEL_ULONG, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.blkread */ + { NULL, + { PMDA_PMID(CLUSTER_DM,3), PM_TYPE_U64, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.blkwrite */ + { NULL, + { PMDA_PMID(CLUSTER_DM,4), PM_TYPE_U64, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.blktotal */ + { NULL, + { PMDA_PMID(CLUSTER_DM,5), PM_TYPE_U64, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DM,6), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* disk.dm.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DM,7), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* disk.dm.total_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_DM,8), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* disk.dm.read_merge */ + { NULL, + { PMDA_PMID(CLUSTER_DM,9), KERNEL_ULONG, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.write_merge */ + { NULL, + { PMDA_PMID(CLUSTER_DM,10), KERNEL_ULONG, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* disk.dm.avactive */ + { NULL, + { PMDA_PMID(CLUSTER_DM,11), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + /* disk.dm.aveq */ + { NULL, + { PMDA_PMID(CLUSTER_DM,12), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + /* hinv.map.dmname */ + { NULL, + { PMDA_PMID(CLUSTER_DM,13), PM_TYPE_STRING, DM_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* disk.dm.read_rawactive */ + { NULL, + { PMDA_PMID(CLUSTER_DM,14), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + /* disk.dm.write_rawactive */ + { NULL, + { PMDA_PMID(CLUSTER_DM,15), PM_TYPE_U32, DM_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, +}; + +char *linux_statspath = ""; /* optional path prefix for all stats files */ + +FILE * +linux_statsfile(const char *path, char *buffer, int size) +{ + snprintf(buffer, size, "%s%s", linux_statspath, path); + buffer[size-1] = '\0'; + return fopen(buffer, "r"); +} + +static void +linux_refresh(pmdaExt *pmda, int *need_refresh) +{ + int need_refresh_mtab = 0; + + if (need_refresh[CLUSTER_PARTITIONS]) + refresh_proc_partitions(INDOM(DISK_INDOM), INDOM(PARTITIONS_INDOM), INDOM(DM_INDOM)); + + if (need_refresh[CLUSTER_STAT]) + refresh_proc_stat(&proc_cpuinfo, &proc_stat); + + if (need_refresh[CLUSTER_CPUINFO]) + refresh_proc_cpuinfo(&proc_cpuinfo); + + if (need_refresh[CLUSTER_MEMINFO]) + refresh_proc_meminfo(&proc_meminfo); + + if (need_refresh[CLUSTER_NUMA_MEMINFO]) + refresh_numa_meminfo(&numa_meminfo, &proc_cpuinfo, &proc_stat); + + if (need_refresh[CLUSTER_LOADAVG]) + refresh_proc_loadavg(&proc_loadavg); + + if (need_refresh[CLUSTER_NET_DEV]) + refresh_proc_net_dev(INDOM(NET_DEV_INDOM)); + + if (need_refresh[CLUSTER_NET_ADDR]) + refresh_net_dev_addr(INDOM(NET_ADDR_INDOM)); + + if (need_refresh[CLUSTER_FILESYS] || need_refresh[CLUSTER_TMPFS]) + refresh_filesys(INDOM(FILESYS_INDOM), INDOM(TMPFS_INDOM)); + + if (need_refresh[CLUSTER_INTERRUPTS] || + need_refresh[CLUSTER_INTERRUPT_LINES] || + need_refresh[CLUSTER_INTERRUPT_OTHER]) + need_refresh_mtab |= refresh_interrupt_values(); + + if (need_refresh[CLUSTER_SWAPDEV]) + refresh_swapdev(INDOM(SWAPDEV_INDOM)); + + if (need_refresh[CLUSTER_NET_NFS]) + refresh_proc_net_rpc(&proc_net_rpc); + + if (need_refresh[CLUSTER_NET_SOCKSTAT]) + refresh_proc_net_sockstat(&proc_net_sockstat); + + if (need_refresh[CLUSTER_KERNEL_UNAME]) + uname(&kernel_uname); + + if (need_refresh[CLUSTER_NET_SNMP]) + refresh_proc_net_snmp(&_pm_proc_net_snmp); + + if (need_refresh[CLUSTER_SCSI]) + refresh_proc_scsi(&proc_scsi); + + if (need_refresh[CLUSTER_LV]) + refresh_dev_mapper(&dev_mapper); + + if (need_refresh[CLUSTER_NET_TCP]) + refresh_proc_net_tcp(&proc_net_tcp); + + if (need_refresh[CLUSTER_NET_NETSTAT]) + refresh_proc_net_netstat(&_pm_proc_net_netstat); + + if (need_refresh[CLUSTER_SLAB]) + refresh_proc_slabinfo(&proc_slabinfo); + + if (need_refresh[CLUSTER_SEM_LIMITS]) + refresh_sem_limits(&sem_limits); + + if (need_refresh[CLUSTER_MSG_LIMITS]) + refresh_msg_limits(&msg_limits); + + if (need_refresh[CLUSTER_SHM_LIMITS]) + refresh_shm_limits(&shm_limits); + + if (need_refresh[CLUSTER_UPTIME]) + refresh_proc_uptime(&proc_uptime); + + if (need_refresh[CLUSTER_VFS]) + refresh_proc_sys_fs(&proc_sys_fs); + + if (need_refresh[CLUSTER_VMSTAT]) + refresh_proc_vmstat(&_pm_proc_vmstat); + + if (need_refresh[CLUSTER_SYSFS_KERNEL]) + refresh_sysfs_kernel(&sysfs_kernel); + + if (need_refresh_mtab) + pmdaDynamicMetricTable(pmda); +} + +static int +linux_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + __pmInDom_int *indomp = (__pmInDom_int *)&indom; + int need_refresh[NUM_CLUSTERS]; + + memset(need_refresh, 0, sizeof(need_refresh)); + switch (indomp->serial) { + case DISK_INDOM: + case PARTITIONS_INDOM: + case DM_INDOM: + need_refresh[CLUSTER_PARTITIONS]++; + break; + case CPU_INDOM: + need_refresh[CLUSTER_STAT]++; + break; + case NODE_INDOM: + need_refresh[CLUSTER_NUMA_MEMINFO]++; + break; + case LOADAVG_INDOM: + need_refresh[CLUSTER_LOADAVG]++; + break; + case NET_DEV_INDOM: + need_refresh[CLUSTER_NET_DEV]++; + break; + case FILESYS_INDOM: + need_refresh[CLUSTER_FILESYS]++; + break; + case TMPFS_INDOM: + need_refresh[CLUSTER_TMPFS]++; + break; + case SWAPDEV_INDOM: + need_refresh[CLUSTER_SWAPDEV]++; + break; + case NFS_INDOM: + case NFS3_INDOM: + case NFS4_CLI_INDOM: + case NFS4_SVR_INDOM: + need_refresh[CLUSTER_NET_NFS]++; + break; + case SCSI_INDOM: + need_refresh[CLUSTER_SCSI]++; + break; + case LV_INDOM: + need_refresh[CLUSTER_LV]++; + break; + case SLAB_INDOM: + need_refresh[CLUSTER_SLAB]++; + break; + case ICMPMSG_INDOM: + need_refresh[CLUSTER_NET_SNMP]++; + break; + /* no default label : pmdaInstance will pick up errors */ + } + + linux_refresh(pmda, need_refresh); + return pmdaInstance(indom, inst, name, result, pmda); +} + +/* + * callback provided to pmdaFetch + */ + +static int +linux_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int i; + int sts; + long sl; + struct filesys *fs; + net_addr_t *addrp; + net_interface_t *netip; + + if (mdesc->m_user != NULL) { + /* + * The metric value is extracted directly via the address specified + * in metrictab. Note: not all metrics support this - those that + * don't have NULL for the m_user field in their respective + * metrictab slot. + */ + if (idp->cluster == CLUSTER_VMSTAT) { + if (!(_pm_have_proc_vmstat) || + *(__uint64_t *)mdesc->m_user == (__uint64_t)-1) + return 0; /* no value available on this kernel */ + } + else + if (idp->cluster == CLUSTER_NET_SNMP) { + __uint64_t value; + + /* network.icmpmsg has an indom - deal with it now */ + if (idp->item == 88 || idp->item == 89) { + if (inst > NR_ICMPMSG_COUNTERS) + return PM_ERR_INST; + value = *((__uint64_t *)mdesc->m_user + inst); + if (value == (__uint64_t)-1) + return 0; /* no value for this instance */ + atom->ull = value; + return 1; + } + if (*(__uint64_t *)mdesc->m_user == (__uint64_t)-1) + if (idp->item != 53) /* tcp.maxconn is special */ + return 0; /* no value available on this kernel */ + } + else + if (idp->cluster == CLUSTER_NET_NETSTAT) { + if (*(__uint64_t *)mdesc->m_user == (__uint64_t)-1) + return 0; /* no value available on this kernel */ + } + else + if (idp->cluster == CLUSTER_NET_NFS) { + /* + * check if rpc stats are available + */ + if (idp->item >= 20 && idp->item <= 27 && proc_net_rpc.client.errcode != 0) + /* no values available for client rpc/nfs - this is expected <= 2.0.36 */ + return 0; + else + if (idp->item >= 30 && idp->item <= 47 && proc_net_rpc.server.errcode != 0) + /* no values available - expected without /proc/net/rpc/nfsd */ + return 0; /* no values available */ + if (idp->item >= 51 && idp->item <= 57 && proc_net_rpc.server.errcode != 0) + /* no values available - expected without /proc/net/rpc/nfsd */ + return 0; /* no values available */ + } + if (idp->cluster == CLUSTER_SYSFS_KERNEL) { + /* no values available for udev metrics */ + if (idp->item == 0 && !sysfs_kernel.valid_uevent_seqnum) { + return 0; + } + } + + switch (mdesc->m_desc.type) { + case PM_TYPE_32: + atom->l = *(__int32_t *)mdesc->m_user; + break; + case PM_TYPE_U32: + atom->ul = *(__uint32_t *)mdesc->m_user; + break; + case PM_TYPE_64: + atom->ll = *(__int64_t *)mdesc->m_user; + break; + case PM_TYPE_U64: + atom->ull = *(__uint64_t *)mdesc->m_user; + break; + case PM_TYPE_FLOAT: + atom->f = *(float *)mdesc->m_user; + break; + case PM_TYPE_DOUBLE: + atom->d = *(double *)mdesc->m_user; + break; + case PM_TYPE_STRING: + atom->cp = (char *)mdesc->m_user; + break; + default: + return 0; + } + } + else + switch (idp->cluster) { + case CLUSTER_STAT: + /* + * All metrics from /proc/stat + */ + switch (idp->item) { + case 0: /* kernel.percpu.cpu.user */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_user[inst] / proc_stat.hz); + break; + case 1: /* kernel.percpu.cpu.nice */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_nice[inst] / proc_stat.hz); + break; + case 2: /* kernel.percpu.cpu.sys */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_sys[inst] / proc_stat.hz); + break; + case 3: /* kernel.percpu.cpu.idle */ + _pm_assign_utype(_pm_idletime_size, atom, + 1000 * (double)proc_stat.p_idle[inst] / proc_stat.hz); + break; + case 30: /* kernel.percpu.cpu.wait.total */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_wait[inst] / proc_stat.hz); + break; + case 31: /* kernel.percpu.cpu.intr */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * ((double)proc_stat.p_irq[inst] + + (double)proc_stat.p_sirq[inst]) / proc_stat.hz); + break; + case 56: /* kernel.percpu.cpu.irq.soft */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_sirq[inst] / proc_stat.hz); + break; + case 57: /* kernel.percpu.cpu.irq.hard */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_irq[inst] / proc_stat.hz); + break; + case 58: /* kernel.percpu.cpu.steal */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_steal[inst] / proc_stat.hz); + break; + case 61: /* kernel.percpu.cpu.guest */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.p_guest[inst] / proc_stat.hz); + break; + case 76: /* kernel.percpu.cpu.vuser */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * ((double)proc_stat.p_user[inst] - (double)proc_stat.p_guest[inst]) + / proc_stat.hz); + break; + case 62: /* kernel.pernode.cpu.user */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_user[inst] / proc_stat.hz); + break; + case 63: /* kernel.pernode.cpu.nice */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_nice[inst] / proc_stat.hz); + break; + case 64: /* kernel.pernode.cpu.sys */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_sys[inst] / proc_stat.hz); + break; + case 65: /* kernel.pernode.cpu.idle */ + _pm_assign_utype(_pm_idletime_size, atom, + 1000 * (double)proc_stat.n_idle[inst] / proc_stat.hz); + break; + case 69: /* kernel.pernode.cpu.wait.total */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_wait[inst] / proc_stat.hz); + break; + case 66: /* kernel.pernode.cpu.intr */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * ((double)proc_stat.n_irq[inst] + + (double)proc_stat.n_sirq[inst]) / proc_stat.hz); + break; + case 70: /* kernel.pernode.cpu.irq.soft */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_sirq[inst] / proc_stat.hz); + break; + case 71: /* kernel.pernode.cpu.irq.hard */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_irq[inst] / proc_stat.hz); + break; + case 67: /* kernel.pernode.cpu.steal */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_steal[inst] / proc_stat.hz); + break; + case 68: /* kernel.pernode.cpu.guest */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.n_guest[inst] / proc_stat.hz); + break; + case 77: /* kernel.pernode.cpu.guest */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * ((double)proc_stat.n_user[inst] - (double)proc_stat.n_guest[inst]) + / proc_stat.hz); + break; + + case 8: /* pagesin */ + if (_pm_have_proc_vmstat) + atom->ul = _pm_proc_vmstat.pswpin; + else + atom->ul = proc_stat.swap[0]; + break; + case 9: /* pagesout */ + if (_pm_have_proc_vmstat) + atom->ul = _pm_proc_vmstat.pswpout; + else + atom->ul = proc_stat.swap[1]; + break; + case 10: /* in */ + if (_pm_have_proc_vmstat) + return PM_ERR_APPVERSION; /* no swap operation counts in 2.6 */ + else + atom->ul = proc_stat.page[0]; + break; + case 11: /* out */ + if (_pm_have_proc_vmstat) + return PM_ERR_APPVERSION; /* no swap operation counts in 2.6 */ + else + atom->ul = proc_stat.page[1]; + break; + case 12: /* intr */ + _pm_assign_utype(_pm_intr_size, atom, proc_stat.intr); + break; + case 13: /* ctxt */ + _pm_assign_utype(_pm_ctxt_size, atom, proc_stat.ctxt); + break; + case 14: /* processes */ + _pm_assign_ulong(atom, proc_stat.processes); + break; + + case 20: /* kernel.all.cpu.user */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.user / proc_stat.hz); + break; + case 21: /* kernel.all.cpu.nice */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.nice / proc_stat.hz); + break; + case 22: /* kernel.all.cpu.sys */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.sys / proc_stat.hz); + break; + case 23: /* kernel.all.cpu.idle */ + _pm_assign_utype(_pm_idletime_size, atom, + 1000 * (double)proc_stat.idle / proc_stat.hz); + break; + case 34: /* kernel.all.cpu.intr */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * ((double)proc_stat.irq + + (double)proc_stat.sirq) / proc_stat.hz); + break; + case 35: /* kernel.all.cpu.wait.total */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.wait / proc_stat.hz); + break; + case 53: /* kernel.all.cpu.irq.soft */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.sirq / proc_stat.hz); + break; + case 54: /* kernel.all.cpu.irq.hard */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.irq / proc_stat.hz); + break; + case 55: /* kernel.all.cpu.steal */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.steal / proc_stat.hz); + break; + case 60: /* kernel.all.cpu.guest */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * (double)proc_stat.guest / proc_stat.hz); + break; + case 78: /* kernel.all.cpu.vuser */ + _pm_assign_utype(_pm_cputime_size, atom, + 1000 * ((double)proc_stat.user - (double)proc_stat.guest) + / proc_stat.hz); + break; + + case 19: /* hinv.nnode */ + atom->ul = indomtab[NODE_INDOM].it_numinst; + break; + case 32: /* hinv.ncpu */ + atom->ul = indomtab[CPU_INDOM].it_numinst; + break; + case 33: /* hinv.ndisk */ + atom->ul = pmdaCacheOp(INDOM(DISK_INDOM), PMDA_CACHE_SIZE_ACTIVE); + break; + + case 48: /* kernel.all.hz */ + atom->ul = proc_stat.hz; + break; + + default: + /* + * Disk metrics used to be fetched from /proc/stat (2.2 kernels) + * but have since moved to /proc/partitions (2.4 kernels) and + * /proc/diskstats (2.6 kernels). We preserve the cluster number + * (middle bits of a PMID) for backward compatibility. + * + * Note that proc_partitions_fetch() will return PM_ERR_PMID + * if we have tried to fetch an unknown metric. + */ + return proc_partitions_fetch(mdesc, inst, atom); + } + break; + + case CLUSTER_UPTIME: /* uptime */ + switch (idp->item) { + case 0: + /* + * kernel.all.uptime (in seconds) + * contributed by "gilly" + * modified by Mike Mason" + */ + atom->ul = proc_uptime.uptime; + break; + case 1: + /* + * kernel.all.idletime (in seconds) + * contributed by "Mike Mason" + */ + atom->ul = proc_uptime.idletime; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_MEMINFO: /* mem */ + switch (idp->item) { + case 0: /* mem.physmem (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.MemTotal >> 10; + break; + case 1: /* mem.util.used (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemTotal) || + !MEMINFO_VALID_VALUE(proc_meminfo.MemFree)) + return 0; /* no values available */ + atom->ull = (proc_meminfo.MemTotal - proc_meminfo.MemFree) >> 10; + break; + case 2: /* mem.util.free (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.MemFree >> 10; + break; + case 3: /* mem.util.shared (in kbytes) */ + /* + * If this metric is exported by the running kernel, it is always + * zero (deprecated). PCP exports it for compatibility with older + * PCP monitoring tools, e.g. pmgsys running on IRIX(TM). + */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemShared)) + return 0; /* no values available */ + atom->ull = proc_meminfo.MemShared >> 10; + break; + case 4: /* mem.util.bufmem (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Buffers)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Buffers >> 10; + break; + case 5: /* mem.util.cached (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Cached)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Cached >> 10; + break; + case 6: /* swap.length (in bytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SwapTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SwapTotal; + break; + case 7: /* swap.used (in bytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SwapTotal) || + !MEMINFO_VALID_VALUE(proc_meminfo.SwapFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SwapTotal - proc_meminfo.SwapFree; + break; + case 8: /* swap.free (in bytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SwapFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SwapFree; + break; + case 9: /* hinv.physmem (in mbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemTotal)) + return 0; /* no values available */ + atom->ul = proc_meminfo.MemTotal >> 20; + break; + case 10: /* mem.freemem (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.MemFree >> 10; + break; + case 11: /* hinv.pagesize (in bytes) */ + atom->ul = _pm_system_pagesize; + break; + case 12: /* mem.util.other (in kbytes) */ + /* other = used - (cached+buffers) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemTotal) || + !MEMINFO_VALID_VALUE(proc_meminfo.MemFree) || + !MEMINFO_VALID_VALUE(proc_meminfo.Cached) || + !MEMINFO_VALID_VALUE(proc_meminfo.Buffers)) + return 0; /* no values available */ + sl = (proc_meminfo.MemTotal - + proc_meminfo.MemFree - + proc_meminfo.Cached - + proc_meminfo.Buffers) >> 10; + atom->ull = sl >= 0 ? sl : 0; + break; + case 13: /* mem.util.swapCached (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SwapCached)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SwapCached >> 10; + break; + case 14: /* mem.util.active (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Active)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Active >> 10; + break; + case 15: /* mem.util.inactive (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Inactive)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Inactive >> 10; + break; + case 16: /* mem.util.highTotal (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HighTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HighTotal >> 10; + break; + case 17: /* mem.util.highFree (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HighFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HighFree >> 10; + break; + case 18: /* mem.util.lowTotal (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.LowTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.LowTotal >> 10; + break; + case 19: /* mem.util.lowFree (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.LowFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.LowFree >> 10; + break; + case 20: /* mem.util.swapTotal (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SwapTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SwapTotal >> 10; + break; + case 21: /* mem.util.swapFree (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SwapFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SwapFree >> 10; + break; + case 22: /* mem.util.dirty (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Dirty)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Dirty >> 10; + break; + case 23: /* mem.util.writeback (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Writeback)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Writeback >> 10; + break; + case 24: /* mem.util.mapped (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Mapped)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Mapped >> 10; + break; + case 25: /* mem.util.slab (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Slab)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Slab >> 10; + break; + case 26: /* mem.util.committed_AS (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Committed_AS)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Committed_AS >> 10; + break; + case 27: /* mem.util.pageTables (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.PageTables)) + return 0; /* no values available */ + atom->ull = proc_meminfo.PageTables >> 10; + break; + case 28: /* mem.util.reverseMaps (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.ReverseMaps)) + return 0; /* no values available */ + atom->ull = proc_meminfo.ReverseMaps >> 10; + break; + case 29: /* mem.util.clean_cache (in kbytes) */ + /* clean=cached-(dirty+writeback) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Cached) || + !MEMINFO_VALID_VALUE(proc_meminfo.Dirty) || + !MEMINFO_VALID_VALUE(proc_meminfo.Writeback)) + return 0; /* no values available */ + sl = (proc_meminfo.Cached - + proc_meminfo.Dirty - + proc_meminfo.Writeback) >> 10; + atom->ull = sl >= 0 ? sl : 0; + break; + case 30: /* mem.util.anonpages */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.AnonPages)) + return 0; /* no values available */ + atom->ull = proc_meminfo.AnonPages >> 10; + break; + case 31: /* mem.util.commitLimit (in kbytes) */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.CommitLimit)) + return 0; /* no values available */ + atom->ull = proc_meminfo.CommitLimit >> 10; + break; + case 32: /* mem.util.bounce */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Bounce)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Bounce >> 10; + break; + case 33: /* mem.util.NFS_Unstable */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.NFS_Unstable)) + return 0; /* no values available */ + atom->ull = proc_meminfo.NFS_Unstable >> 10; + break; + case 34: /* mem.util.slabReclaimable */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SlabReclaimable)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SlabReclaimable >> 10; + break; + case 35: /* mem.util.slabUnreclaimable */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.SlabUnreclaimable)) + return 0; /* no values available */ + atom->ull = proc_meminfo.SlabUnreclaimable >> 10; + break; + case 36: /* mem.util.active_anon */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Active_anon)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Active_anon >> 10; + break; + case 37: /* mem.util.inactive_anon */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Inactive_anon)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Inactive_anon >> 10; + break; + case 38: /* mem.util.active_file */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Active_file)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Active_file >> 10; + break; + case 39: /* mem.util.inactive_file */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Inactive_file)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Inactive_file >> 10; + break; + case 40: /* mem.util.unevictable */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Unevictable)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Unevictable >> 10; + break; + case 41: /* mem.util.mlocked */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Mlocked)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Mlocked >> 10; + break; + case 42: /* mem.util.shmem */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Shmem)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Shmem >> 10; + break; + case 43: /* mem.util.kernelStack */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.KernelStack)) + return 0; /* no values available */ + atom->ull = proc_meminfo.KernelStack >> 10; + break; + case 44: /* mem.util.hugepagesTotal */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HugepagesTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HugepagesTotal; + break; + case 45: /* mem.util.hugepagesFree */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HugepagesFree)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HugepagesFree; + break; + case 46: /* mem.util.hugepagesRsvd */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HugepagesRsvd)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HugepagesRsvd; + break; + case 47: /* mem.util.hugepagesSurp */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HugepagesSurp)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HugepagesSurp; + break; + case 48: /* mem.util.directMap4k */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.directMap4k)) + return 0; /* no values available */ + atom->ull = proc_meminfo.directMap4k >> 10; + break; + case 49: /* mem.util.directMap2M */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.directMap2M)) + return 0; /* no values available */ + atom->ull = proc_meminfo.directMap2M >> 10; + break; + case 50: /* mem.util.vmallocTotal */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.VmallocTotal)) + return 0; /* no values available */ + atom->ull = proc_meminfo.VmallocTotal >> 10; + break; + case 51: /* mem.util.vmallocUsed */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.VmallocUsed)) + return 0; /* no values available */ + atom->ull = proc_meminfo.VmallocUsed >> 10; + break; + case 52: /* mem.util.vmallocChunk */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.VmallocChunk)) + return 0; /* no values available */ + atom->ull = proc_meminfo.VmallocChunk >> 10; + break; + case 53: /* mem.util.mmap_copy */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MmapCopy)) + return 0; /* no values available */ + atom->ull = proc_meminfo.MmapCopy >> 10; + break; + case 54: /* mem.util.quicklists */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.Quicklists)) + return 0; /* no values available */ + atom->ull = proc_meminfo.Quicklists >> 10; + break; + case 55: /* mem.util.corrupthardware */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.HardwareCorrupted)) + return 0; /* no values available */ + atom->ull = proc_meminfo.HardwareCorrupted >> 10; + break; + case 56: /* mem.util.anonhugepages */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.AnonHugePages)) + return 0; /* no values available */ + atom->ull = proc_meminfo.AnonHugePages >> 10; + break; + case 57: /* mem.util.directMap1G */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.directMap1G)) + return 0; /* no values available */ + atom->ull = proc_meminfo.directMap1G >> 10; + break; + case 58: /* mem.util.available */ + if (!MEMINFO_VALID_VALUE(proc_meminfo.MemAvailable)) + return 0; /* no values available */ + atom->ull = proc_meminfo.MemAvailable >> 10; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_LOADAVG: + switch(idp->item) { + case 0: /* kernel.all.load */ + if (inst == 1) + atom->f = proc_loadavg.loadavg[0]; + else + if (inst == 5) + atom->f = proc_loadavg.loadavg[1]; + else + if (inst == 15) + atom->f = proc_loadavg.loadavg[2]; + else + return PM_ERR_INST; + break; + case 1: /* kernel.all.lastpid -- added by "Mike Mason" */ + atom->ul = proc_loadavg.lastpid; + break; + case 2: /* kernel.all.runnable */ + atom->ul = proc_loadavg.runnable; + break; + case 3: /* kernel.all.nprocs */ + atom->ul = proc_loadavg.nprocs; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_NET_DEV: /* network.interface */ + if (idp->item == 27) { /* hinv.ninterface */ + atom->ul = pmdaCacheOp(INDOM(NET_DEV_INDOM), PMDA_CACHE_SIZE_ACTIVE); + break; + } + sts = pmdaCacheLookup(INDOM(NET_DEV_INDOM), inst, NULL, (void **)&netip); + if (sts < 0) + return sts; + if (idp->item <= 15) { + /* network.interface.{in,out} */ + atom->ull = netip->counters[idp->item]; + } + else + switch (idp->item) { + case 16: /* network.interface.total.bytes */ + atom->ull = netip->counters[0] + netip->counters[8]; + break; + case 17: /* network.interface.total.packets */ + atom->ull = netip->counters[1] + netip->counters[9]; + break; + case 18: /* network.interface.total.errors */ + atom->ull = netip->counters[2] + netip->counters[10]; + break; + case 19: /* network.interface.total.drops */ + atom->ull = netip->counters[3] + netip->counters[11]; + break; + case 20: /* network.interface.total.mcasts */ + /* + * NOTE: there is no network.interface.out.mcasts metric + * so this total only includes network.interface.in.mcasts + */ + atom->ull = netip->counters[7]; + break; + case 21: /* network.interface.mtu */ + if (!netip->ioc.mtu) + return 0; + atom->ul = netip->ioc.mtu; + break; + case 22: /* network.interface.speed */ + if (!netip->ioc.speed) + return 0; + atom->f = ((float)netip->ioc.speed * 1000000) / 8 / 1024 / 1024; + break; + case 23: /* network.interface.baudrate */ + if (!netip->ioc.speed) + return 0; + atom->ul = ((long long)netip->ioc.speed * 1000000 / 8); + break; + case 24: /* network.interface.duplex */ + if (!netip->ioc.duplex) + return 0; + atom->ul = netip->ioc.duplex; + break; + case 25: /* network.interface.up */ + atom->ul = netip->ioc.linkup; + break; + case 26: /* network.interface.running */ + atom->ul = netip->ioc.running; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_NET_ADDR: + sts = pmdaCacheLookup(INDOM(NET_ADDR_INDOM), inst, NULL, (void **)&addrp); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + switch (idp->item) { + case 0: /* network.interface.inet_addr */ + if (addrp->has_inet == 0) + return 0; + atom->cp = addrp->inet; + break; + case 1: /* network.interface.ipv6_addr */ + if (addrp->has_ipv6 == 0) + return 0; + atom->cp = addrp->ipv6; + break; + case 2: /* network.interface.ipv6_scope */ + if (addrp->has_ipv6 == 0) + return 0; + atom->cp = lookup_ipv6_scope(addrp->ipv6scope); + break; + case 3: /* network.interface.hw_addr */ + if (addrp->has_hw == 0) + return 0; + atom->cp = addrp->hw_addr; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_FILESYS: + if (idp->item == 0) + atom->ul = pmdaCacheOp(INDOM(FILESYS_INDOM), PMDA_CACHE_SIZE_ACTIVE); + else { + struct statfs *sbuf; + __uint64_t ull, used; + + sts = pmdaCacheLookup(INDOM(FILESYS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + sbuf = &fs->stats; + if (!(fs->flags & FSF_FETCHED)) { + if (statfs(fs->path, sbuf) < 0) + return PM_ERR_INST; + fs->flags |= FSF_FETCHED; + } + + switch (idp->item) { + case 1: /* filesys.capacity */ + ull = (__uint64_t)sbuf->f_blocks; + atom->ull = ull * sbuf->f_bsize / 1024; + break; + case 2: /* filesys.used */ + used = (__uint64_t)(sbuf->f_blocks - sbuf->f_bfree); + atom->ull = used * sbuf->f_bsize / 1024; + break; + case 3: /* filesys.free */ + ull = (__uint64_t)sbuf->f_bfree; + atom->ull = ull * sbuf->f_bsize / 1024; + break; + case 4: /* filesys.maxfiles */ + atom->ul = sbuf->f_files; + break; + case 5: /* filesys.usedfiles */ + atom->ul = sbuf->f_files - sbuf->f_ffree; + break; + case 6: /* filesys.freefiles */ + atom->ul = sbuf->f_ffree; + break; + case 7: /* filesys.mountdir */ + atom->cp = fs->path; + break; + case 8: /* filesys.full */ + used = (__uint64_t)(sbuf->f_blocks - sbuf->f_bfree); + ull = used + (__uint64_t)sbuf->f_bavail; + atom->d = (100.0 * (double)used) / (double)ull; + break; + case 9: /* filesys.blocksize -- added by Mike Mason */ + atom->ul = sbuf->f_bsize; + break; + case 10: /* filesys.avail -- added by Mike Mason */ + ull = (__uint64_t)sbuf->f_bavail; + atom->ull = ull * sbuf->f_bsize / 1024; + break; + case 11: /* filesys.readonly */ + atom->ul = (scan_filesys_options(fs->options, "ro") != NULL); + break; + default: + return PM_ERR_PMID; + } + } + break; + + case CLUSTER_TMPFS: { + struct statfs *sbuf; + __uint64_t ull, used; + + sts = pmdaCacheLookup(INDOM(TMPFS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + sbuf = &fs->stats; + if (!(fs->flags & FSF_FETCHED)) { + if (statfs(fs->path, sbuf) < 0) + return PM_ERR_INST; + fs->flags |= FSF_FETCHED; + } + + switch (idp->item) { + case 1: /* tmpfs.capacity */ + ull = (__uint64_t)sbuf->f_blocks; + atom->ull = ull * sbuf->f_bsize / 1024; + break; + case 2: /* tmpfs.used */ + used = (__uint64_t)(sbuf->f_blocks - sbuf->f_bfree); + atom->ull = used * sbuf->f_bsize / 1024; + break; + case 3: /* tmpfs.free */ + ull = (__uint64_t)sbuf->f_bfree; + atom->ull = ull * sbuf->f_bsize / 1024; + break; + case 4: /* tmpfs.maxfiles */ + atom->ul = sbuf->f_files; + break; + case 5: /* tmpfs.usedfiles */ + atom->ul = sbuf->f_files - sbuf->f_ffree; + break; + case 6: /* tmpfs.freefiles */ + atom->ul = sbuf->f_ffree; + break; + case 7: /* tmpfs.full */ + used = (__uint64_t)(sbuf->f_blocks - sbuf->f_bfree); + ull = used + (__uint64_t)sbuf->f_bavail; + atom->d = (100.0 * (double)used) / (double)ull; + break; + default: + return PM_ERR_PMID; + } + } + break; + + case CLUSTER_SWAPDEV: { + struct swapdev *swap; + + sts = pmdaCacheLookup(INDOM(SWAPDEV_INDOM), inst, NULL, (void **)&swap); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + switch (idp->item) { + case 0: /* swapdev.free (kbytes) */ + atom->ul = swap->size - swap->used; + break; + case 1: /* swapdev.length (kbytes) */ + case 2: /* swapdev.maxswap (kbytes) */ + atom->ul = swap->size; + break; + case 3: /* swapdev.vlength (kbytes) */ + atom->ul = 0; + break; + case 4: /* swapdev.priority */ + atom->l = swap->priority; + break; + default: + return PM_ERR_PMID; + } + break; + } + + case CLUSTER_NET_NFS: + switch (idp->item) { + case 1: /* nfs.client.calls */ + if (proc_net_rpc.client.errcode != 0) + return 0; /* no values available */ + for (atom->ul=0, i=0; i < NR_RPC_COUNTERS; i++) { + atom->ul += proc_net_rpc.client.reqcounts[i]; + } + break; + case 50: /* nfs.server.calls */ + if (proc_net_rpc.server.errcode != 0) + return 0; /* no values available */ + for (atom->ul=0, i=0; i < NR_RPC_COUNTERS; i++) { + atom->ul += proc_net_rpc.server.reqcounts[i]; + } + break; + case 4: /* nfs.client.reqs */ + if (proc_net_rpc.client.errcode != 0) + return 0; /* no values available */ + if (inst < NR_RPC_COUNTERS) + atom->ul = proc_net_rpc.client.reqcounts[inst]; + else + return PM_ERR_INST; + break; + + case 12: /* nfs.server.reqs */ + if (proc_net_rpc.server.errcode != 0) + return 0; /* no values available */ + if (inst < NR_RPC_COUNTERS) + atom->ul = proc_net_rpc.server.reqcounts[inst]; + else + return PM_ERR_INST; + break; + + case 60: /* nfs3.client.calls */ + if (proc_net_rpc.client.errcode != 0) + return 0; /* no values available */ + for (atom->ul=0, i=0; i < NR_RPC3_COUNTERS; i++) { + atom->ul += proc_net_rpc.client.reqcounts3[i]; + } + break; + + case 62: /* nfs3.server.calls */ + if (proc_net_rpc.server.errcode != 0) + return 0; /* no values available */ + for (atom->ul=0, i=0; i < NR_RPC3_COUNTERS; i++) { + atom->ul += proc_net_rpc.server.reqcounts3[i]; + } + break; + + case 61: /* nfs3.client.reqs */ + if (proc_net_rpc.client.errcode != 0) + return 0; /* no values available */ + if (inst < NR_RPC3_COUNTERS) + atom->ul = proc_net_rpc.client.reqcounts3[inst]; + else + return PM_ERR_INST; + break; + + case 63: /* nfs3.server.reqs */ + if (proc_net_rpc.server.errcode != 0) + return 0; /* no values available */ + if (inst < NR_RPC3_COUNTERS) + atom->ul = proc_net_rpc.server.reqcounts3[inst]; + else + return PM_ERR_INST; + break; + + case 64: /* nfs4.client.calls */ + if (proc_net_rpc.client.errcode != 0) + return 0; /* no values available */ + for (atom->ul=0, i=0; i < NR_RPC4_CLI_COUNTERS; i++) { + atom->ul += proc_net_rpc.client.reqcounts4[i]; + } + break; + + case 66: /* nfs4.server.calls */ + if (proc_net_rpc.server.errcode != 0) + return 0; /* no values available */ + for (atom->ul=0, i=0; i < NR_RPC4_SVR_COUNTERS; i++) { + atom->ul += proc_net_rpc.server.reqcounts4[i]; + } + break; + + case 65: /* nfs4.client.reqs */ + if (proc_net_rpc.client.errcode != 0) + return 0; /* no values available */ + if (inst < NR_RPC4_CLI_COUNTERS) + atom->ul = proc_net_rpc.client.reqcounts4[inst]; + else + return PM_ERR_INST; + break; + + case 67: /* nfs4.server.reqs */ + if (proc_net_rpc.server.errcode != 0) + return 0; /* no values available */ + if (inst < NR_RPC4_SVR_COUNTERS) + atom->ul = proc_net_rpc.server.reqcounts4[inst]; + else + return PM_ERR_INST; + break; + + /* + * Note: all other rpc metric values are extracted directly via the + * address specified in the metrictab (see above) + */ + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_SLAB: + if (proc_slabinfo.ncaches == 0) + return 0; /* no values available */ + + if (inst >= proc_slabinfo.ncaches) + return PM_ERR_INST; + + switch(idp->item) { + case 0: /* mem.slabinfo.objects.active */ + atom->ull = proc_slabinfo.caches[inst].num_active_objs; + break; + case 1: /* mem.slabinfo.objects.total */ + atom->ull = proc_slabinfo.caches[inst].total_objs; + break; + case 2: /* mem.slabinfo.objects.size */ + if (proc_slabinfo.caches[inst].seen < 11) /* version 1.1 or later only */ + return 0; + atom->ul = proc_slabinfo.caches[inst].object_size; + break; + case 3: /* mem.slabinfo.slabs.active */ + if (proc_slabinfo.caches[inst].seen < 11) /* version 1.1 or later only */ + return 0; + atom->ul = proc_slabinfo.caches[inst].num_active_slabs; + break; + case 4: /* mem.slabinfo.slabs.total */ + if (proc_slabinfo.caches[inst].seen == 11) /* version 1.1 only */ + return 0; + atom->ul = proc_slabinfo.caches[inst].total_slabs; + break; + case 5: /* mem.slabinfo.slabs.pages_per_slab */ + if (proc_slabinfo.caches[inst].seen < 11) /* version 1.1 or later only */ + return 0; + atom->ul = proc_slabinfo.caches[inst].pages_per_slab; + break; + case 6: /* mem.slabinfo.slabs.objects_per_slab */ + if (proc_slabinfo.caches[inst].seen != 20) /* version 2.0 only */ + return 0; + atom->ul = proc_slabinfo.caches[inst].objects_per_slab; + break; + case 7: /* mem.slabinfo.slabs.total_size */ + if (proc_slabinfo.caches[inst].seen < 11) /* version 1.1 or later only */ + return 0; + atom->ull = proc_slabinfo.caches[inst].total_size; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_PARTITIONS: + return proc_partitions_fetch(mdesc, inst, atom); + + case CLUSTER_SCSI: + if (proc_scsi.nscsi == 0) + return 0; /* no values available */ + switch(idp->item) { + case 0: /* hinv.map.scsi */ + atom->cp = (char *)NULL; + for (i=0; i < proc_scsi.nscsi; i++) { + if (proc_scsi.scsi[i].id == inst) { + atom->cp = proc_scsi.scsi[i].dev_name; + break; + } + } + if (i == proc_scsi.nscsi) + return PM_ERR_INST; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_LV: + switch(idp->item) { + case 0: /* hinv.map.lvname */ + if (dev_mapper.nlv == 0) + return 0; /* no values available */ + atom->cp = (char *)NULL; + for (i = 0; i < dev_mapper.nlv; i++) { + if (dev_mapper.lv[i].id == inst) { + atom->cp = dev_mapper.lv[i].dev_name; + break; + } + } + if (i == dev_mapper.nlv) + return PM_ERR_INST; + break; + case 1: /* hinv.nlv */ + atom->ul = dev_mapper.nlv; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_KERNEL_UNAME: + switch(idp->item) { + case 5: /* pmda.uname */ + sprintf(uname_string, "%s %s %s %s %s", + kernel_uname.sysname, + kernel_uname.nodename, + kernel_uname.release, + kernel_uname.version, + kernel_uname.machine); + atom->cp = uname_string; + break; + + case 6: /* pmda.version */ + atom->cp = pmGetConfig("PCP_VERSION"); + break; + + case 7: /* kernel.uname.distro ... not from uname(2) */ + atom->cp = get_distro_info(); + break; + + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_CPUINFO: + if (idp->item != 7 && /* hinv.machine is singular */ + (inst >= proc_cpuinfo.cpuindom->it_numinst)) + return PM_ERR_INST; + switch(idp->item) { + case 0: /* hinv.cpu.clock */ + if (proc_cpuinfo.cpuinfo[inst].clock == 0.0) + return 0; + atom->f = proc_cpuinfo.cpuinfo[inst].clock; + break; + case 1: /* hinv.cpu.vendor */ + i = proc_cpuinfo.cpuinfo[inst].vendor; + atom->cp = linux_strings_lookup(i); + if (atom->cp == NULL) + atom->cp = "unknown"; + break; + case 2: /* hinv.cpu.model */ + if ((i = proc_cpuinfo.cpuinfo[inst].model) < 0) + i = proc_cpuinfo.cpuinfo[inst].model_name; + atom->cp = linux_strings_lookup(i); + if (atom->cp == NULL) + atom->cp = "unknown"; + break; + case 3: /* hinv.cpu.stepping */ + i = proc_cpuinfo.cpuinfo[inst].stepping; + atom->cp = linux_strings_lookup(i); + if (atom->cp == NULL) + atom->cp = "unknown"; + break; + case 4: /* hinv.cpu.cache */ + if (!proc_cpuinfo.cpuinfo[inst].cache) + return 0; + atom->ul = proc_cpuinfo.cpuinfo[inst].cache; + break; + case 5: /* hinv.cpu.bogomips */ + if (proc_cpuinfo.cpuinfo[inst].bogomips == 0.0) + return 0; + atom->f = proc_cpuinfo.cpuinfo[inst].bogomips; + break; + case 6: /* hinv.map.cpu_num */ + atom->ul = proc_cpuinfo.cpuinfo[inst].cpu_num; + break; + case 7: /* hinv.machine */ + atom->cp = proc_cpuinfo.machine; + break; + case 8: /* hinv.map.cpu_node */ + atom->ul = proc_cpuinfo.cpuinfo[inst].node; + break; + case 9: /* hinv.cpu.model_name */ + if ((i = proc_cpuinfo.cpuinfo[inst].model_name) < 0) + i = proc_cpuinfo.cpuinfo[inst].model; + atom->cp = linux_strings_lookup(i); + if (atom->cp == NULL) + atom->cp = "unknown"; + break; + case 10: /* hinv.cpu.flags */ + i = proc_cpuinfo.cpuinfo[inst].flags; + atom->cp = linux_strings_lookup(i); + if (atom->cp == NULL) + atom->cp = "unknown"; + break; + case 11: /* hinv.cpu.cache_alignment */ + if (!proc_cpuinfo.cpuinfo[inst].cache_align) + return 0; + atom->ul = proc_cpuinfo.cpuinfo[inst].cache_align; + break; + default: + return PM_ERR_PMID; + } + break; + + /* + * Cluster added by Mike Mason + */ + case CLUSTER_SEM_LIMITS: + switch (idp->item) { + case 0: /* ipc.sem.max_semmap */ + atom->ul = sem_limits.semmap; + break; + case 1: /* ipc.sem.max_semid */ + atom->ul = sem_limits.semmni; + break; + case 2: /* ipc.sem.max_sem */ + atom->ul = sem_limits.semmns; + break; + case 3: /* ipc.sem.num_undo */ + atom->ul = sem_limits.semmnu; + break; + case 4: /* ipc.sem.max_perid */ + atom->ul = sem_limits.semmsl; + break; + case 5: /* ipc.sem.max_ops */ + atom->ul = sem_limits.semopm; + break; + case 6: /* ipc.sem.max_undoent */ + atom->ul = sem_limits.semume; + break; + case 7: /* ipc.sem.sz_semundo */ + atom->ul = sem_limits.semusz; + break; + case 8: /* ipc.sem.max_semval */ + atom->ul = sem_limits.semvmx; + break; + case 9: /* ipc.sem.max_exit */ + atom->ul = sem_limits.semaem; + break; + default: + return PM_ERR_PMID; + } + break; + + /* + * Cluster added by Mike Mason + */ + case CLUSTER_MSG_LIMITS: + switch (idp->item) { + case 0: /* ipc.msg.sz_pool */ + atom->ul = msg_limits.msgpool; + break; + case 1: /* ipc.msg.mapent */ + atom->ul = msg_limits.msgmap; + break; + case 2: /* ipc.msg.max_msgsz */ + atom->ul = msg_limits.msgmax; + break; + case 3: /* ipc.msg.max_defmsgq */ + atom->ul = msg_limits.msgmnb; + break; + case 4: /* ipc.msg.max_msgqid */ + atom->ul = msg_limits.msgmni; + break; + case 5: /* ipc.msg.sz_msgseg */ + atom->ul = msg_limits.msgssz; + break; + case 6: /* ipc.msg.num_smsghdr */ + atom->ul = msg_limits.msgtql; + break; + case 7: /* ipc.msg.max_seg */ + atom->ul = (unsigned long) msg_limits.msgseg; + break; + default: + return PM_ERR_PMID; + } + break; + + /* + * Cluster added by Mike Mason + */ + case CLUSTER_SHM_LIMITS: + switch (idp->item) { + case 0: /* ipc.shm.max_segsz */ + atom->ul = shm_limits.shmmax; + break; + case 1: /* ipc.shm.min_segsz */ + atom->ul = shm_limits.shmmin; + break; + case 2: /* ipc.shm.max_seg */ + atom->ul = shm_limits.shmmni; + break; + case 3: /* ipc.shm.max_segproc */ + atom->ul = shm_limits.shmseg; + break; + case 4: /* ipc.shm.max_shmsys */ + atom->ul = shm_limits.shmall; + break; + default: + return PM_ERR_PMID; + } + break; + + /* + * Cluster added by Mike Mason + */ + case CLUSTER_NUSERS: + { + /* count the number of users */ + struct utmp *ut; + atom->ul = 0; + setutent(); + while ((ut = getutent())) { + if ((ut->ut_type == USER_PROCESS) && (ut->ut_name[0] != '\0')) + atom->ul++; + } + endutent(); + } + break; + + + case CLUSTER_IB: /* deprecated: network.ib, use infiniband PMDA */ + return PM_ERR_APPVERSION; + + case CLUSTER_NUMA_MEMINFO: + /* NUMA memory metrics from /sys/devices/system/node/nodeX */ + if (inst >= numa_meminfo.node_indom->it_numinst) + return PM_ERR_INST; + + switch(idp->item) { + case 0: /* mem.numa.util.total */ + sts = linux_table_lookup("MemTotal:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + case 1: /* mem.numa.util.free */ + sts = linux_table_lookup("MemFree:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 2: /* mem.numa.util.used */ + sts = linux_table_lookup("MemUsed:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 3: /* mem.numa.util.active */ + sts = linux_table_lookup("Active:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 4: /* mem.numa.util.inactive */ + sts = linux_table_lookup("Inactive:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 5: /* mem.numa.util.active_anon */ + sts = linux_table_lookup("Active(anon):", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 6: /* mem.numa.util.inactive_anon */ + sts = linux_table_lookup("Inactive(anon):", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 7: /* mem.numa.util.active_file */ + sts = linux_table_lookup("Active(file):", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 8: /* mem.numa.util.inactive_file */ + sts = linux_table_lookup("Inactive(file):", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 9: /* mem.numa.util.highTotal */ + sts = linux_table_lookup("HighTotal:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 10: /* mem.numa.util.highFree */ + sts = linux_table_lookup("HighFree:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 11: /* mem.numa.util.lowTotal */ + sts = linux_table_lookup("LowTotal:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 12: /* mem.numa.util.lowFree */ + sts = linux_table_lookup("LowFree:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 13: /* mem.numa.util.unevictable */ + sts = linux_table_lookup("Unevictable:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 14: /* mem.numa.util.mlocked */ + sts = linux_table_lookup("Mlocked:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 15: /* mem.numa.util.dirty */ + sts = linux_table_lookup("Dirty:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 16: /* mem.numa.util.writeback */ + sts = linux_table_lookup("Writeback:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 17: /* mem.numa.util.filePages */ + sts = linux_table_lookup("FilePages:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 18: /* mem.numa.util.mapped */ + sts = linux_table_lookup("Mapped:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 19: /* mem.numa.util.anonPages */ + sts = linux_table_lookup("AnonPages:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 20: /* mem.numa.util.shmem */ + sts = linux_table_lookup("Shmem:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 21: /* mem.numa.util.kernelStack */ + sts = linux_table_lookup("KernelStack:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 22: /* mem.numa.util.pageTables */ + sts = linux_table_lookup("PageTables:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 23: /* mem.numa.util.NFS_Unstable */ + sts = linux_table_lookup("NFS_Unstable:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 24: /* mem.numa.util.bounce */ + sts = linux_table_lookup("Bounce:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 25: /* mem.numa.util.writebackTmp */ + sts = linux_table_lookup("WritebackTmp:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 26: /* mem.numa.util.slab */ + sts = linux_table_lookup("Slab:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 27: /* mem.numa.util.slabReclaimable */ + sts = linux_table_lookup("SReclaimable:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 28: /* mem.numa.util.slabUnreclaimable */ + sts = linux_table_lookup("SUnreclaim:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 29: /* mem.numa.util.hugepagesTotal */ + sts = linux_table_lookup("HugePages_Total:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 30: /* mem.numa.util.hugepagesFree */ + sts = linux_table_lookup("HugePages_Free:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 31: /* mem.numa.util.hugepagesSurp */ + sts = linux_table_lookup("HugePages_Surp:", numa_meminfo.node_info[inst].meminfo, + &atom->ull); + break; + + case 32: /* mem.numa.alloc.hit */ + sts = linux_table_lookup("numa_hit", numa_meminfo.node_info[inst].memstat, + &atom->ull); + break; + + case 33: /* mem.numa.alloc.miss */ + sts = linux_table_lookup("numa_miss", numa_meminfo.node_info[inst].memstat, + &atom->ull); + break; + + case 34: /* mem.numa.alloc.foreign */ + sts = linux_table_lookup("numa_foreign", numa_meminfo.node_info[inst].memstat, + &atom->ull); + break; + + case 35: /* mem.numa.alloc.interleave_hit */ + sts = linux_table_lookup("interleave_hit", numa_meminfo.node_info[inst].memstat, + &atom->ull); + break; + + case 36: /* mem.numa.alloc.local_node */ + sts = linux_table_lookup("local_node", numa_meminfo.node_info[inst].memstat, + &atom->ull); + break; + + case 37: /* mem.numa.alloc.other_node */ + sts = linux_table_lookup("other_node", numa_meminfo.node_info[inst].memstat, + &atom->ull); + break; + + default: + return PM_ERR_PMID; + } + return sts; + + case CLUSTER_INTERRUPTS: + switch (idp->item) { + case 3: /* kernel.all.interrupts.error */ + atom->ul = irq_err_count; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_INTERRUPT_LINES: + case CLUSTER_INTERRUPT_OTHER: + if (inst >= indomtab[CPU_INDOM].it_numinst) + return PM_ERR_INST; + return interrupts_fetch(idp->cluster, idp->item, inst, atom); + + case CLUSTER_DM: + return proc_partitions_fetch(mdesc, inst, atom); + break; + + default: /* unknown cluster */ + return PM_ERR_PMID; + } + + return 1; +} + + +static int +linux_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; + int need_refresh[NUM_CLUSTERS]; + + memset(need_refresh, 0, sizeof(need_refresh)); + for (i=0; i < numpmid; i++) { + __pmID_int *idp = (__pmID_int *)&(pmidlist[i]); + if (idp->cluster < NUM_CLUSTERS) { + need_refresh[idp->cluster]++; + + if ((idp->cluster == CLUSTER_STAT || idp->cluster == CLUSTER_DM) && + need_refresh[CLUSTER_PARTITIONS] == 0 && + is_partitions_metric(pmidlist[i])) + need_refresh[CLUSTER_PARTITIONS]++; + + if (idp->cluster == CLUSTER_CPUINFO || + idp->cluster == CLUSTER_INTERRUPT_LINES || + idp->cluster == CLUSTER_INTERRUPT_OTHER || + idp->cluster == CLUSTER_INTERRUPTS) + need_refresh[CLUSTER_STAT]++; + } + + /* In 2.6 kernels, swap.{pagesin,pagesout} are in /proc/vmstat */ + if (_pm_have_proc_vmstat && idp->cluster == CLUSTER_STAT) { + if (idp->item >= 8 && idp->item <= 11) + need_refresh[CLUSTER_VMSTAT]++; + } + } + + linux_refresh(pmda, need_refresh); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +linux_text(int ident, int type, char **buf, pmdaExt *pmda) +{ + if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) { + int sts = pmdaDynamicLookupText(ident, type, buf, pmda); + if (sts != -ENOENT) + return sts; + } + return pmdaText(ident, type, buf, pmda); +} + +static int +linux_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + pmdaNameSpace *tree = pmdaDynamicLookupName(pmda, name); + return pmdaTreePMID(tree, name, pmid); +} + +static int +linux_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + pmdaNameSpace *tree = pmdaDynamicLookupPMID(pmda, pmid); + return pmdaTreeName(tree, pmid, nameset); +} + +static int +linux_children(const char *name, int flag, char ***kids, int **sts, pmdaExt *pmda) +{ + pmdaNameSpace *tree = pmdaDynamicLookupName(pmda, name); + return pmdaTreeChildren(tree, name, flag, kids, sts); +} + +pmInDom +linux_indom(int serial) +{ + return indomtab[serial].it_indom; +} + +pmdaIndom * +linux_pmda_indom(int serial) +{ + return &indomtab[serial]; +} + +/* + * Helper routines for accessing a generic static string dictionary + */ + +char * +linux_strings_lookup(int index) +{ + char *value; + pmInDom dict = INDOM(STRINGS_INDOM); + + if (pmdaCacheLookup(dict, index, &value, NULL) == PMDA_CACHE_ACTIVE) + return value; + return NULL; +} + +int +linux_strings_insert(const char *buf) +{ + pmInDom dict = INDOM(STRINGS_INDOM); + return pmdaCacheStore(dict, PMDA_CACHE_ADD, buf, NULL); +} + +/* + * Initialise the agent (both daemon and DSO). + */ + +void +__PMDA_INIT_CALL +linux_init(pmdaInterface *dp) +{ + int i, major, minor, point; + size_t nmetrics, nindoms; + char *envpath; + __pmID_int *idp; + + _pm_system_pagesize = getpagesize(); + if ((envpath = getenv("LINUX_STATSPATH")) != NULL) + linux_statspath = envpath; + + if (_isDSO) { + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(helppath, sizeof(helppath), "%s%c" "linux" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_4, "linux DSO", helppath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.four.instance = linux_instance; + dp->version.four.fetch = linux_fetch; + dp->version.four.text = linux_text; + dp->version.four.pmid = linux_pmid; + dp->version.four.name = linux_name; + dp->version.four.children = linux_children; + pmdaSetFetchCallBack(dp, linux_fetchCallBack); + + proc_stat.cpu_indom = proc_cpuinfo.cpuindom = &indomtab[CPU_INDOM]; + numa_meminfo.node_indom = proc_cpuinfo.node_indom = &indomtab[NODE_INDOM]; + proc_scsi.scsi_indom = &indomtab[SCSI_INDOM]; + dev_mapper.lv_indom = &indomtab[LV_INDOM]; + proc_slabinfo.indom = &indomtab[SLAB_INDOM]; + + /* + * Figure out kernel version. The precision of certain metrics + * (e.g. percpu time counters) has changed over kernel versions. + * See include/linux/kernel_stat.h for all the various flavours. + */ + uname(&kernel_uname); + _pm_ctxt_size = 8; + _pm_intr_size = 8; + _pm_cputime_size = 8; + _pm_idletime_size = 8; + if (sscanf(kernel_uname.release, "%d.%d.%d", &major, &minor, &point) == 3) { + if (major < 2 || (major == 2 && minor <= 4)) { /* 2.4 and earlier */ + _pm_ctxt_size = 4; + _pm_intr_size = 4; + _pm_cputime_size = 4; + _pm_idletime_size = sizeof(unsigned long); + } + else if (major == 2 && minor == 6 && + point >= 0 && point <= 4) { /* 2.6.0->.4 */ + _pm_cputime_size = 4; + _pm_idletime_size = 4; + } + } + for (i = 0; i < sizeof(metrictab)/sizeof(pmdaMetric); i++) { + idp = (__pmID_int *)&(metrictab[i].m_desc.pmid); + if (idp->cluster == CLUSTER_STAT) { + switch (idp->item) { + case 0: /* kernel.percpu.cpu.user */ + case 1: /* kernel.percpu.cpu.nice */ + case 2: /* kernel.percpu.cpu.sys */ + case 20: /* kernel.all.cpu.user */ + case 21: /* kernel.all.cpu.nice */ + case 22: /* kernel.all.cpu.sys */ + case 30: /* kernel.percpu.cpu.wait.total */ + case 31: /* kernel.percpu.cpu.intr */ + case 34: /* kernel.all.cpu.intr */ + case 35: /* kernel.all.cpu.wait.total */ + case 53: /* kernel.all.cpu.irq.soft */ + case 54: /* kernel.all.cpu.irq.hard */ + case 55: /* kernel.all.cpu.steal */ + case 56: /* kernel.percpu.cpu.irq.soft */ + case 57: /* kernel.percpu.cpu.irq.hard */ + case 58: /* kernel.percpu.cpu.steal */ + case 60: /* kernel.all.cpu.guest */ + case 78: /* kernel.all.cpu.vuser */ + case 61: /* kernel.percpu.cpu.guest */ + case 76: /* kernel.percpu.cpu.vuser */ + case 62: /* kernel.pernode.cpu.user */ + case 63: /* kernel.pernode.cpu.nice */ + case 64: /* kernel.pernode.cpu.sys */ + case 69: /* kernel.pernode.cpu.wait.total */ + case 66: /* kernel.pernode.cpu.intr */ + case 70: /* kernel.pernode.cpu.irq.soft */ + case 71: /* kernel.pernode.cpu.irq.hard */ + case 67: /* kernel.pernode.cpu.steal */ + case 68: /* kernel.pernode.cpu.guest */ + case 77: /* kernel.pernode.cpu.vuser */ + _pm_metric_type(metrictab[i].m_desc.type, _pm_cputime_size); + break; + case 3: /* kernel.percpu.cpu.idle */ + case 23: /* kernel.all.cpu.idle */ + case 65: /* kernel.pernode.cpu.idle */ + _pm_metric_type(metrictab[i].m_desc.type, _pm_idletime_size); + break; + case 12: /* kernel.all.intr */ + _pm_metric_type(metrictab[i].m_desc.type, _pm_intr_size); + break; + case 13: /* kernel.all.pswitch */ + _pm_metric_type(metrictab[i].m_desc.type, _pm_ctxt_size); + break; + } + } + if (metrictab[i].m_desc.type == PM_TYPE_NOSUPPORT) + fprintf(stderr, "Bad kernel metric descriptor type (%u.%u)\n", + idp->cluster, idp->item); + } + + nindoms = sizeof(indomtab)/sizeof(indomtab[0]); + nmetrics = sizeof(metrictab)/sizeof(metrictab[0]); + + proc_vmstat_init(); + interrupts_init(metrictab, nmetrics); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, indomtab, nindoms, metrictab, nmetrics); + + /* string metrics use the pmdaCache API for value indexing */ + pmdaCacheOp(INDOM(STRINGS_INDOM), PMDA_CACHE_STRINGS); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + + _isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(helppath, sizeof(helppath), "%s%c" "linux" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, LINUX, "linux.log", helppath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + linux_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/linux/proc_cpuinfo.c b/src/pmdas/linux/proc_cpuinfo.c new file mode 100644 index 0000000..f76ac27 --- /dev/null +++ b/src/pmdas/linux/proc_cpuinfo.c @@ -0,0 +1,246 @@ +/* + * Linux /proc/cpuinfo metrics cluster + * + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. + * Portions Copyright (c) 2001 Gilly Ran (gilly@exanet.com) - for the + * portions supporting the Alpha platform. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "proc_cpuinfo.h" + + +static void +decode_map(proc_cpuinfo_t *proc_cpuinfo, char *cp, int node, int offset) +{ + uint32_t map = strtoul(cp, NULL, 16); + + while (map) { + int i; + + if ((i = ffsl(map))) { + /* the kernel returns 32bit words in the map file */ + int cpu = i - 1 + 32*offset; + + proc_cpuinfo->cpuinfo[cpu].node = node; + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cpu %d -> node %d\n", + cpu, node); + } + map &= ~(1 << (i-1)); + } + } +} + +static void +map_cpu_nodes(proc_cpuinfo_t *proc_cpuinfo) +{ + int i, j; + const char *node_path = "sys/devices/system/node"; + char path[MAXPATHLEN]; + char cpumap[4096]; + DIR *nodes; + FILE *f; + struct dirent *de; + int node, max_node = -1; + char *cp; + pmdaIndom *idp = PMDAINDOM(NODE_INDOM); + + for (i = 0; i < proc_cpuinfo->cpuindom->it_numinst; i++) + proc_cpuinfo->cpuinfo[i].node = -1; + + snprintf(path, sizeof(path), "%s/%s", linux_statspath, node_path); + if ((nodes = opendir(path)) == NULL) + return; + + while ((de = readdir(nodes)) != NULL) { + if (sscanf(de->d_name, "node%d", &node) != 1) + continue; + + if (node > max_node) + max_node = node; + + snprintf(path, sizeof(path), "%s/%s/%s/cpumap", + linux_statspath, node_path, de->d_name); + if ((f = fopen(path, "r")) == NULL) + continue; + i = fscanf(f, "%s", cpumap); + fclose(f); + if (i != 1) + continue; + + for (j = 0; (cp = strrchr(cpumap, ',')); j++) { + decode_map(proc_cpuinfo, cp+1, node, j); + *cp = '\0'; + } + decode_map(proc_cpuinfo, cpumap, node, j); + } + closedir(nodes); + + /* initialize node indom */ + idp->it_numinst = max_node + 1; + idp->it_set = calloc(max_node + 1, sizeof(pmdaInstid)); + for (i = 0; i <= max_node; i++) { + char node_name[256]; + + sprintf(node_name, "node%d", i); + idp->it_set[i].i_inst = i; + idp->it_set[i].i_name = strdup(node_name); + } + proc_cpuinfo->node_indom = idp; +} + +char * +cpu_name(proc_cpuinfo_t *proc_cpuinfo, int c) +{ + char name[1024]; + char *p; + FILE *f; + static int started = 0; + + if (!started) { + refresh_proc_cpuinfo(proc_cpuinfo); + + proc_cpuinfo->machine = NULL; + f = linux_statsfile("/proc/sgi_prominfo/node0/version", name, sizeof(name)); + if (f != NULL) { + while (fgets(name, sizeof(name), f)) { + if (strncmp(name, "SGI", 3) == 0) { + if ((p = strstr(name, " IP")) != NULL) + proc_cpuinfo->machine = strndup(p+1, 4); + break; + } + } + fclose(f); + } + if (proc_cpuinfo->machine == NULL) + proc_cpuinfo->machine = strdup("linux"); + + started = 1; + } + + snprintf(name, sizeof(name), "cpu%d", c); + return strdup(name); +} + +int +refresh_proc_cpuinfo(proc_cpuinfo_t *proc_cpuinfo) +{ + char buf[4096]; + FILE *fp; + int cpunum; + cpuinfo_t *info; + char *val; + char *p; + static int started = 0; + + if (!started) { + int need = proc_cpuinfo->cpuindom->it_numinst * sizeof(cpuinfo_t); + proc_cpuinfo->cpuinfo = (cpuinfo_t *)calloc(1, need); + for (cpunum=0; cpunum < proc_cpuinfo->cpuindom->it_numinst; cpunum++) { + proc_cpuinfo->cpuinfo[cpunum].sapic = -1; + proc_cpuinfo->cpuinfo[cpunum].vendor = -1; + proc_cpuinfo->cpuinfo[cpunum].model = -1; + proc_cpuinfo->cpuinfo[cpunum].model_name = -1; + proc_cpuinfo->cpuinfo[cpunum].stepping = -1; + proc_cpuinfo->cpuinfo[cpunum].flags = -1; + } + started = 1; + } + + if ((fp = linux_statsfile("/proc/cpuinfo", buf, sizeof(buf))) == NULL) + return -oserror(); + +#if defined(HAVE_ALPHA_LINUX) + cpunum = 0; +#else //intel + cpunum = -1; +#endif + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((val = strrchr(buf, '\n')) != NULL) + *val = '\0'; + if ((val = strchr(buf, ':')) == NULL) + continue; + val += 2; + +#if !defined(HAVE_ALPHA_LINUX) + if (strncmp(buf, "processor", 9) == 0) { + cpunum++; + proc_cpuinfo->cpuinfo[cpunum].cpu_num = atoi(val); + continue; + } +#endif + + if (cpunum < 0 || cpunum >= proc_cpuinfo->cpuindom->it_numinst) + continue; + + info = &proc_cpuinfo->cpuinfo[cpunum]; + + /* note: order is important due to strNcmp comparisons */ + if (info->sapic < 0 && strncasecmp(buf, "sapic", 5) == 0) + info->sapic = linux_strings_insert(val); + else if (info->model_name < 0 && strncasecmp(buf, "model name", 10) == 0) + info->model_name = linux_strings_insert(val); + else if (info->model < 0 && strncasecmp(buf, "model", 5) == 0) + info->model = linux_strings_insert(val); + else if (info->model < 0 && strncasecmp(buf, "cpu model", 9) == 0) + info->model = linux_strings_insert(val); + else if (info->vendor < 0 && strncasecmp(buf, "vendor", 6) == 0) + info->vendor = linux_strings_insert(val); + else if (info->stepping < 0 && strncasecmp(buf, "step", 4) == 0) + info->stepping = linux_strings_insert(val); + else if (info->stepping < 0 && strncasecmp(buf, "revision", 8) == 0) + info->stepping = linux_strings_insert(val); + else if (info->stepping < 0 && strncasecmp(buf, "cpu revision", 12) == 0) + info->stepping = linux_strings_insert(val); + else if (info->flags < 0 && strncasecmp(buf, "flags", 5) == 0) + info->flags = linux_strings_insert(val); + else if (info->flags < 0 && strncasecmp(buf, "features", 8) == 0) + info->flags = linux_strings_insert(val); + else if (info->cache == 0 && strncasecmp(buf, "cache size", 10) == 0) + info->cache = atoi(val); + else if (info->cache_align == 0 && strncasecmp(buf, "cache_align", 11) == 0) + info->cache_align = atoi(val); + else if (info->bogomips == 0.0 && strncasecmp(buf, "bogo", 4) == 0) + info->bogomips = atof(val); + else if (info->clock == 0.0 && strncasecmp(buf, "cpu MHz", 7) == 0) + info->clock = atof(val); + else if (info->clock == 0.0 && strncasecmp(buf, "cycle frequency", 15) == 0) { + if ((p = strchr(val, ' ')) != NULL) + *p = '\0'; + info->clock = (atof(val))/1000000; + } + } + fclose(fp); + +#if defined(HAVE_ALPHA_LINUX) + /* all processors are identical, therefore duplicate it to all the instances */ + for (cpunum = 1; cpunum < proc_cpuinfo->cpuindom->it_numinst; cpunum++) + memcpy(&proc_cpuinfo->cpuinfo[cpunum], info, sizeof(cpuinfo_t)); +#endif + + if (started < 2) { + map_cpu_nodes(proc_cpuinfo); + started = 2; + } + + /* success */ + return 0; +} diff --git a/src/pmdas/linux/proc_cpuinfo.h b/src/pmdas/linux/proc_cpuinfo.h new file mode 100644 index 0000000..b0691b4 --- /dev/null +++ b/src/pmdas/linux/proc_cpuinfo.h @@ -0,0 +1,49 @@ +/* + * Linux /proc/cpuinfo metrics cluster + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2001 Gilly Ran (gilly@exanet.com) for the + * portions of the code supporting the Alpha platform. + * All rights reserved. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifdef __alpha__ +#define HAVE_ALPHA_LINUX +#endif + +typedef struct { + int cpu_num; + int node; + char *name; + float clock; + float bogomips; + int sapic; /* strings dictionary hash key */ + int vendor; /* strings dictionary hash key */ + int model; /* strings dictionary hash key */ + int model_name; /* strings dictionary hash key */ + int stepping; /* strings dictionary hash key */ + int flags; /* strings dictionary hash key */ + unsigned int cache; + unsigned int cache_align; +} cpuinfo_t; + +typedef struct { + char *machine; + cpuinfo_t *cpuinfo; + pmdaIndom *cpuindom; + pmdaIndom *node_indom; +} proc_cpuinfo_t; + +extern int refresh_proc_cpuinfo(proc_cpuinfo_t *); +extern char *cpu_name(proc_cpuinfo_t *, int); diff --git a/src/pmdas/linux/proc_loadavg.c b/src/pmdas/linux/proc_loadavg.c new file mode 100644 index 0000000..676cfc6 --- /dev/null +++ b/src/pmdas/linux/proc_loadavg.c @@ -0,0 +1,45 @@ +/* + * Linux /proc/loadavg metrics cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include "proc_loadavg.h" + +int +refresh_proc_loadavg(proc_loadavg_t *proc_loadavg) +{ + char buf[1024]; + FILE *fp; + + if ((fp = linux_statsfile("/proc/loadavg", buf, sizeof(buf))) == NULL) + return -oserror(); + + if (fgets(buf, sizeof(buf), fp) == NULL) + return -oserror(); + fclose(fp); + + /* + * 0.00 0.00 0.05 1/67 17563 + * Lastpid added by Mike Mason + */ + sscanf((const char *)buf, "%f %f %f %u/%u %u", + &proc_loadavg->loadavg[0], &proc_loadavg->loadavg[1], + &proc_loadavg->loadavg[2], &proc_loadavg->runnable, + &proc_loadavg->nprocs, &proc_loadavg->lastpid); + return 0; +} diff --git a/src/pmdas/linux/proc_loadavg.h b/src/pmdas/linux/proc_loadavg.h new file mode 100644 index 0000000..15152c8 --- /dev/null +++ b/src/pmdas/linux/proc_loadavg.h @@ -0,0 +1,29 @@ +/* + * Linux /proc/stat metrics cluster + * + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct { + float loadavg[3]; /* 1, 5 and 15 min load average */ + unsigned int runnable; + unsigned int nprocs; + unsigned int lastpid; +} proc_loadavg_t; + +extern int refresh_proc_loadavg(proc_loadavg_t *); + diff --git a/src/pmdas/linux/proc_meminfo.c b/src/pmdas/linux/proc_meminfo.c new file mode 100644 index 0000000..471e2ce --- /dev/null +++ b/src/pmdas/linux/proc_meminfo.c @@ -0,0 +1,188 @@ +/* + * Linux /proc/meminfo metrics cluster + * + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include +#include "proc_meminfo.h" + +static proc_meminfo_t moff; +extern size_t _pm_system_pagesize; + +static struct { + char *field; + int64_t *offset; +} meminfo_fields[] = { + { "MemTotal", &moff.MemTotal }, + { "MemFree", &moff.MemFree }, + { "MemAvailable", &moff.MemAvailable }, + { "MemShared", &moff.MemShared }, + { "Buffers", &moff.Buffers }, + { "Cached", &moff.Cached }, + { "SwapCached", &moff.SwapCached }, + { "Active", &moff.Active }, + { "Inactive", &moff.Inactive }, + { "Active(anon)", &moff.Active_anon }, + { "Inactive(anon)", &moff.Inactive_anon }, + { "Active(file)", &moff.Active_file }, + { "Inactive(file)", &moff.Inactive_file }, + { "Unevictable", &moff.Unevictable }, + { "Mlocked", &moff.Mlocked }, + { "HighTotal", &moff.HighTotal }, + { "HighFree", &moff.HighFree }, + { "LowTotal", &moff.LowTotal }, + { "LowFree", &moff.LowFree }, + { "MmapCopy", &moff.MmapCopy }, + { "SwapTotal", &moff.SwapTotal }, + { "SwapFree", &moff.SwapFree }, + { "Dirty", &moff.Dirty }, + { "Writeback", &moff.Writeback }, + { "AnonPages", &moff.AnonPages }, + { "Mapped", &moff.Mapped }, + { "Shmem", &moff.Shmem }, + { "Slab", &moff.Slab }, + { "SReclaimable", &moff.SlabReclaimable }, + { "SUnreclaim", &moff.SlabUnreclaimable }, + { "KernelStack", &moff.KernelStack }, + { "PageTables", &moff.PageTables }, + { "Quicklists", &moff.Quicklists }, + { "NFS_Unstable", &moff.NFS_Unstable }, + { "Bounce", &moff.Bounce }, + { "WritebackTmp", &moff.WritebackTmp }, + { "CommitLimit", &moff.CommitLimit }, + { "Committed_AS", &moff.Committed_AS }, + { "VmallocTotal", &moff.VmallocTotal }, + { "VmallocUsed", &moff.VmallocUsed }, + { "VmallocChunk", &moff.VmallocChunk }, + { "HardwareCorrupted", &moff.HardwareCorrupted }, + { "AnonHugePages", &moff.AnonHugePages }, + /* vendor kernel patches, some outdated now */ + { "MemShared", &moff.MemShared }, + { "ReverseMaps", &moff.ReverseMaps }, + { "HugePages_Total", &moff.HugepagesTotal }, + { "HugePages_Free", &moff.HugepagesFree }, + { "HugePages_Rsvd", &moff.HugepagesRsvd }, + { "HugePages_Surp", &moff.HugepagesSurp }, + { "DirectMap4k", &moff.directMap4k }, + { "DirectMap2M", &moff.directMap2M }, + { "DirectMap1G", &moff.directMap1G }, + { NULL, NULL } +}; + +#define MOFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)meminfo_fields[ii].offset - (__psint_t)&moff) + +int +refresh_proc_meminfo(proc_meminfo_t *proc_meminfo) +{ + char buf[1024]; + char *bufp; + int64_t *p; + int i; + FILE *fp; + + for (i = 0; meminfo_fields[i].field != NULL; i++) { + p = MOFFSET(i, proc_meminfo); + *p = -1; /* marked as "no value available" */ + } + + if ((fp = linux_statsfile("/proc/meminfo", buf, sizeof(buf))) == NULL) + return -oserror(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((bufp = strchr(buf, ':')) == NULL) + continue; + *bufp = '\0'; + for (i=0; meminfo_fields[i].field != NULL; i++) { + if (strcmp(buf, meminfo_fields[i].field) != 0) + continue; + p = MOFFSET(i, proc_meminfo); + for (bufp++; *bufp; bufp++) { + if (isdigit((int)*bufp)) { + sscanf(bufp, "%llu", (unsigned long long *)p); + *p *= 1024; /* kbytes -> bytes */ + break; + } + } + } + } + + fclose(fp); + + /* + * MemAvailable is only in 3.x or later kernels but we can calculate it + * using other values, similar to upstream kernel commit 34e431b0ae. + * The environment variable is for QA purposes. + */ + if (!MEMINFO_VALID_VALUE(proc_meminfo->MemAvailable) || + getenv("PCP_QA_ESTIMATE_MEMAVAILABLE") != NULL) { + if (MEMINFO_VALID_VALUE(proc_meminfo->MemTotal) && + MEMINFO_VALID_VALUE(proc_meminfo->MemFree) && + MEMINFO_VALID_VALUE(proc_meminfo->Active_file) && + MEMINFO_VALID_VALUE(proc_meminfo->Inactive_file) && + MEMINFO_VALID_VALUE(proc_meminfo->SlabReclaimable)) { + + int64_t pagecache; + int64_t wmark_low = 0; + + /* + * sum for each zone->watermark[WMARK_LOW]; + */ + if ((fp = fopen("/proc/zoneinfo", "r")) != NULL) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((bufp = strstr(buf, "low ")) != NULL) { + int64_t low; + if (sscanf(bufp+4, "%lld", (long long int *)&low) == 1) + wmark_low += low; + } + } + fclose(fp); + wmark_low *= _pm_system_pagesize; + } + + /* + * Free memory cannot be taken below the low watermark, before the + * system starts swapping. + */ + proc_meminfo->MemAvailable = proc_meminfo->MemFree - wmark_low; + + /* + * Not all the page cache can be freed, otherwise the system will + * start swapping. Assume at least half of the page cache, or the + * low watermark worth of cache, needs to stay. + */ + pagecache = proc_meminfo->Active_file + proc_meminfo->Inactive_file; + pagecache -= MIN(pagecache / 2, wmark_low); + proc_meminfo->MemAvailable += pagecache; + + /* + * Part of the reclaimable slab consists of items that are in use, + * and cannot be freed. Cap this estimate at the low watermark. + */ + proc_meminfo->MemAvailable += proc_meminfo->SlabReclaimable; + proc_meminfo->MemAvailable -= MIN(proc_meminfo->SlabReclaimable / 2, wmark_low); + + if (proc_meminfo->MemAvailable < 0) + proc_meminfo->MemAvailable = 0; + } + } + + /* success */ + return 0; +} diff --git a/src/pmdas/linux/proc_meminfo.h b/src/pmdas/linux/proc_meminfo.h new file mode 100644 index 0000000..3185dcc --- /dev/null +++ b/src/pmdas/linux/proc_meminfo.h @@ -0,0 +1,79 @@ +/* + * Linux /proc/meminfo metrics cluster + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define MEMINFO_VALID_VALUE(x) ((x) != (int64_t)-1) +#define MEMINFO_VALUE_OR_ZERO(x) (((x) == (int64_t)-1) ? 0 : (x)) + +/* + * All fields in /proc/meminfo + */ +typedef struct { + int64_t MemTotal; + int64_t MemFree; + int64_t MemAvailable; + int64_t MemShared; + int64_t Buffers; + int64_t Cached; + int64_t SwapCached; + int64_t Active; + int64_t Inactive; + int64_t Active_anon; + int64_t Inactive_anon; + int64_t Active_file; + int64_t Inactive_file; + int64_t Unevictable; + int64_t Mlocked; + int64_t HighTotal; + int64_t HighFree; + int64_t LowTotal; + int64_t LowFree; + int64_t MmapCopy; + int64_t SwapTotal; + int64_t SwapFree; + int64_t SwapUsed; /* computed */ + int64_t Dirty; + int64_t Writeback; + int64_t Mapped; + int64_t Shmem; + int64_t Slab; + int64_t SlabReclaimable; + int64_t SlabUnreclaimable; + int64_t KernelStack; + int64_t CommitLimit; + int64_t Committed_AS; + int64_t PageTables; + int64_t Quicklists; + int64_t ReverseMaps; + int64_t AnonPages; + int64_t Bounce; + int64_t NFS_Unstable; + int64_t WritebackTmp; + int64_t VmallocTotal; + int64_t VmallocUsed; + int64_t VmallocChunk; + int64_t HardwareCorrupted; + int64_t AnonHugePages; + int64_t HugepagesTotal; + int64_t HugepagesFree; + int64_t HugepagesRsvd; + int64_t HugepagesSurp; + int64_t directMap4k; + int64_t directMap2M; + int64_t directMap1G; +} proc_meminfo_t; + +extern int refresh_proc_meminfo(proc_meminfo_t *); diff --git a/src/pmdas/linux/proc_net_dev.c b/src/pmdas/linux/proc_net_dev.c new file mode 100644 index 0000000..93e3057 --- /dev/null +++ b/src/pmdas/linux/proc_net_dev.c @@ -0,0 +1,444 @@ +/* + * Linux /proc/net/dev metrics cluster + * + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include +#include +#include +#include +#include "proc_net_dev.h" + +static int +refresh_inet_socket() +{ + static int netfd = -1; + if (netfd < 0) + netfd = socket(AF_INET, SOCK_DGRAM, 0); + return netfd; +} + +static int +refresh_net_dev_ioctl(char *name, net_interface_t *netip) +{ + struct ethtool_cmd ecmd = { 0 }; + /* + * Note: + * Initialization of ecmd is not really needed. If the ioctl()s + * work, ecmd is filled in ... but valgrind (at least up to + * version 3.9.0) does not know about the SIOCETHTOOL ioctl() + * and thinks the use of ecmd after this call propagates + * uninitialized data in to ioc.speed and ioc.duplex, causing + * failures for qa/957 + * - Ken McDonell, 11 Apr 2014 + */ + struct ifreq ifr; + int fd; + + if ((fd = refresh_inet_socket()) < 0) + return 0; + + ecmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)&ecmd; + strncpy(ifr.ifr_name, name, IF_NAMESIZE); + ifr.ifr_name[IF_NAMESIZE-1] = '\0'; + if (!(ioctl(fd, SIOCGIFMTU, &ifr) < 0)) + netip->ioc.mtu = ifr.ifr_mtu; + + ecmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)&ecmd; + strncpy(ifr.ifr_name, name, IF_NAMESIZE); + ifr.ifr_name[IF_NAMESIZE-1] = '\0'; + if (!(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)) { + netip->ioc.linkup = !!(ifr.ifr_flags & IFF_UP); + netip->ioc.running = !!(ifr.ifr_flags & IFF_RUNNING); + } + /* ETHTOOL ioctl -> non-root permissions issues for old kernels */ + ecmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)&ecmd; + strncpy(ifr.ifr_name, name, IF_NAMESIZE); + ifr.ifr_name[IF_NAMESIZE-1] = '\0'; + if (!(ioctl(fd, SIOCETHTOOL, &ifr) < 0)) { + /* + * speed is defined in ethtool.h and returns the speed in + * Mbps, so 100 for 100Mbps, 1000 for 1Gbps, etc + */ + netip->ioc.speed = ecmd.speed; + netip->ioc.duplex = ecmd.duplex + 1; + return 0; + } + return -ENOSYS; /* caller should try ioctl alternatives */ +} + +static void +refresh_net_ipv4_addr(char *name, net_addr_t *addr) +{ + struct ifreq ifr; + int fd; + + if ((fd = refresh_inet_socket()) < 0) + return; + strncpy(ifr.ifr_name, name, IF_NAMESIZE); + ifr.ifr_name[IF_NAMESIZE-1] = '\0'; + ifr.ifr_addr.sa_family = AF_INET; + if (ioctl(fd, SIOCGIFADDR, &ifr) >= 0) { + struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; + if (inet_ntop(AF_INET, &sin->sin_addr, addr->inet, INET_ADDRSTRLEN)) + addr->has_inet = 1; + } +} + +/* + * No ioctl support or no permissions (more likely), so we + * fall back to grovelling about in /sys/class/net in a last + * ditch attempt to find the ethtool interface data (duplex + * and speed). + */ +static char * +read_oneline(const char *path, char *buffer) +{ + FILE *fp = fopen(path, "r"); + + if (fp) { + int i = fscanf(fp, "%63s", buffer); + fclose(fp); + if (i == 1) + return buffer; + } + return ""; +} + +static void +refresh_net_dev_sysfs(char *name, net_interface_t *netip) +{ + char path[MAXPATHLEN]; + char line[64]; + char *duplex; + + snprintf(path, sizeof(path), "%s/sys/class/net/%s/speed", linux_statspath, name); + path[sizeof(path)-1] = '\0'; + netip->ioc.speed = atoi(read_oneline(path, line)); + + snprintf(path, sizeof(path), "%s/sys/class/net/%s/duplex", linux_statspath, name); + path[sizeof(path)-1] = '\0'; + duplex = read_oneline(path, line); + + if (strcmp(duplex, "full") == 0) + netip->ioc.duplex = 2; + else if (strcmp(duplex, "half") == 0) + netip->ioc.duplex = 1; + else /* eh? */ + netip->ioc.duplex = 0; +} + +static void +refresh_net_hw_addr(char *name, net_addr_t *netip) +{ + char path[MAXPATHLEN]; + char line[64]; + char *value; + + snprintf(path, sizeof(path), "%s/sys/class/net/%s/address", linux_statspath, name); + path[sizeof(path)-1] = '\0'; + + value = read_oneline(path, line); + + if (value[0] != '\0') + netip->has_hw = 1; + strncpy(netip->hw_addr, value, sizeof(netip->hw_addr)); + netip->hw_addr[sizeof(netip->hw_addr)-1] = '\0'; +} + +int +refresh_proc_net_dev(pmInDom indom) +{ + char buf[1024]; + FILE *fp; + unsigned long long llval; + char *p, *v; + int j, sts; + net_interface_t *netip; + + static uint64_t gen; /* refresh generation number */ + static uint32_t cache_err; + + if ((fp = linux_statsfile("/proc/net/dev", buf, sizeof(buf))) == NULL) + return -oserror(); + + if (gen == 0) { + /* + * first time, reload cache from external file, and force any + * subsequent changes to be saved + */ + pmdaCacheOp(indom, PMDA_CACHE_LOAD); + } + gen++; + + /* +Inter-| Receive | Transmit + face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed + lo: 4060748 39057 0 0 0 0 0 0 4060748 39057 0 0 0 0 0 0 + eth0: 0 337614 0 0 0 0 0 0 0 267537 0 0 0 27346 62 0 + */ + + pmdaCacheOp(indom, PMDA_CACHE_INACTIVE); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((p = v = strchr(buf, ':')) == NULL) + continue; + *p = '\0'; + for (p=buf; *p && isspace((int)*p); p++) {;} + + sts = pmdaCacheLookupName(indom, p, NULL, (void **)&netip); + if (sts == PM_ERR_INST || (sts >= 0 && netip == NULL)) { + /* first time since re-loaded, else new one */ + netip = (net_interface_t *)calloc(1, sizeof(net_interface_t)); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_proc_net_dev: initialize \"%s\"\n", p); + } +#endif + } + else if (sts < 0) { + if (cache_err++ < 10) { + fprintf(stderr, "refresh_proc_net_dev: pmdaCacheLookupName(%s, %s, ...) failed: %s\n", + pmInDomStr(indom), p, pmErrStr(sts)); + } + continue; + } + if (netip->last_gen != gen-1) { + /* + * rediscovered one that went away and has returned + * + * kernel counters are reset, so clear last_counters to + * avoid false overflows + */ + for (j=0; j < PROC_DEV_COUNTERS_PER_LINE; j++) { + netip->last_counters[j] = 0; + } + } + netip->last_gen = gen; + if ((sts = pmdaCacheStore(indom, PMDA_CACHE_ADD, p, (void *)netip)) < 0) { + if (cache_err++ < 10) { + fprintf(stderr, "refresh_proc_net_dev: pmdaCacheStore(%s, PMDA_CACHE_ADD, %s, " PRINTF_P_PFX "%p) failed: %s\n", + pmInDomStr(indom), p, netip, pmErrStr(sts)); + } + continue; + } + + /* Issue ioctls for remaining data, not exported through proc */ + memset(&netip->ioc, 0, sizeof(netip->ioc)); + if (refresh_net_dev_ioctl(p, netip) < 0) + refresh_net_dev_sysfs(p, netip); + + for (p=v, j=0; j < PROC_DEV_COUNTERS_PER_LINE; j++) { + for (; !isdigit((int)*p); p++) {;} + sscanf(p, "%llu", &llval); + if (llval >= netip->last_counters[j]) { + netip->counters[j] += + llval - netip->last_counters[j]; + } + else { + /* 32bit counter has wrapped */ + netip->counters[j] += + llval + (UINT_MAX - netip->last_counters[j]); + } + netip->last_counters[j] = llval; + for (; !isspace((int)*p); p++) {;} + } + } + + pmdaCacheOp(indom, PMDA_CACHE_SAVE); + + /* success */ + fclose(fp); + return 0; +} + +static int +refresh_net_dev_ipv4_addr(pmInDom indom) +{ + int n, fd, sts, numreqs = 30; + struct ifconf ifc; + struct ifreq *ifr; + net_addr_t *netip; + static uint32_t cache_err; + + if ((fd = refresh_inet_socket()) < 0) + return fd; + + ifc.ifc_buf = NULL; + for (;;) { + ifc.ifc_len = sizeof(struct ifreq) * numreqs; + ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len); + + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + free(ifc.ifc_buf); + return -oserror(); + } + if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) { + /* assume it overflowed and try again */ + numreqs *= 2; + continue; + } + break; + } + + for (n = 0, ifr = ifc.ifc_req; + n < ifc.ifc_len; + n += sizeof(struct ifreq), ifr++) { + sts = pmdaCacheLookupName(indom, ifr->ifr_name, NULL, (void **)&netip); + if (sts == PM_ERR_INST || (sts >= 0 && netip == NULL)) { + /* first time since re-loaded, else new one */ + netip = (net_addr_t *)calloc(1, sizeof(net_addr_t)); + } + else if (sts < 0) { + if (cache_err++ < 10) { + fprintf(stderr, "refresh_net_dev_ipv4_addr: " + "pmdaCacheLookupName(%s, %s, ...) failed: %s\n", + pmInDomStr(indom), ifr->ifr_name, pmErrStr(sts)); + } + continue; + } + if ((sts = pmdaCacheStore(indom, PMDA_CACHE_ADD, ifr->ifr_name, (void *)netip)) < 0) { + if (cache_err++ < 10) { + fprintf(stderr, "refresh_net_dev_ipv4_addr: " + "pmdaCacheStore(%s, PMDA_CACHE_ADD, %s, " + PRINTF_P_PFX "%p) failed: %s\n", + pmInDomStr(indom), ifr->ifr_name, netip, pmErrStr(sts)); + } + continue; + } + + refresh_net_ipv4_addr(ifr->ifr_name, netip); + refresh_net_hw_addr(ifr->ifr_name, netip); + } + free(ifc.ifc_buf); + return 0; +} + +static int +refresh_net_dev_ipv6_addr(pmInDom indom) +{ + FILE *fp; + char addr6p[8][5]; + char addr6[40], devname[20+1]; + char addr[INET6_ADDRSTRLEN]; + char buf[MAXPATHLEN]; + struct sockaddr_in6 sin6; + int sts, plen, scope, dad_status, if_idx; + net_addr_t *netip; + static uint32_t cache_err; + + if ((fp = linux_statsfile("/proc/net/if_inet6", buf, sizeof(buf))) == NULL) + return 0; + + while (fscanf(fp, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n", + addr6p[0], addr6p[1], addr6p[2], addr6p[3], + addr6p[4], addr6p[5], addr6p[6], addr6p[7], + &if_idx, &plen, &scope, &dad_status, devname) != EOF) { + sts = pmdaCacheLookupName(indom, devname, NULL, (void **)&netip); + if (sts == PM_ERR_INST || (sts >= 0 && netip == NULL)) { + /* first time since re-loaded, else new one */ + netip = (net_addr_t *)calloc(1, sizeof(net_addr_t)); + } + else if (sts < 0) { + if (cache_err++ < 10) { + fprintf(stderr, "refresh_net_dev_ipv6_addr: " + "pmdaCacheLookupName(%s, %s, ...) failed: %s\n", + pmInDomStr(indom), devname, pmErrStr(sts)); + } + continue; + } + if ((sts = pmdaCacheStore(indom, PMDA_CACHE_ADD, devname, (void *)netip)) < 0) { + if (cache_err++ < 10) { + fprintf(stderr, "refresh_net_dev_ipv6_addr: " + "pmdaCacheStore(%s, PMDA_CACHE_ADD, %s, " + PRINTF_P_PFX "%p) failed: %s\n", + pmInDomStr(indom), devname, netip, pmErrStr(sts)); + } + continue; + } + + sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", + addr6p[0], addr6p[1], addr6p[2], addr6p[3], + addr6p[4], addr6p[5], addr6p[6], addr6p[7]); + if (inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr) != 1) + continue; + + sin6.sin6_family = AF_INET6; + sin6.sin6_port = 0; + if (!inet_ntop(AF_INET6, &sin6.sin6_addr, addr, INET6_ADDRSTRLEN)) + continue; + snprintf(netip->ipv6, sizeof(netip->ipv6), "%s/%d", addr, plen); + netip->ipv6scope = (uint16_t)scope; + netip->has_ipv6 = 1; + + refresh_net_hw_addr(devname, netip); + } + fclose(fp); + return 0; +} + +/* + * This separate indom provides the addresses for all interfaces including + * aliases (e.g. eth0, eth0:0, eth0:1, etc) - this is what ifconfig does. + */ +int +refresh_net_dev_addr(pmInDom indom) +{ + int sts = 0; + net_addr_t*p; + + for (pmdaCacheOp(indom, PMDA_CACHE_WALK_REWIND);;) { + if ((sts = pmdaCacheOp(indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + if (!pmdaCacheLookup(indom, sts, NULL, (void **)&p) || !p) + continue; + p->has_inet = 0; + p->has_ipv6 = 0; + p->has_hw = 0; + } + + pmdaCacheOp(indom, PMDA_CACHE_INACTIVE); + + sts |= refresh_net_dev_ipv4_addr(indom); + sts |= refresh_net_dev_ipv6_addr(indom); + + pmdaCacheOp(indom, PMDA_CACHE_SAVE); + return sts; +} + +char * +lookup_ipv6_scope(int scope) +{ + switch (scope) { + case IPV6_ADDR_ANY: + return "Global"; + case IPV6_ADDR_LINKLOCAL: + return "Link"; + case IPV6_ADDR_SITELOCAL: + return "Site"; + case IPV6_ADDR_COMPATv4: + return "Compat"; + case IPV6_ADDR_LOOPBACK: + return "Host"; + } + return "Unknown"; +} diff --git a/src/pmdas/linux/proc_net_dev.h b/src/pmdas/linux/proc_net_dev.h new file mode 100644 index 0000000..9bb09f3 --- /dev/null +++ b/src/pmdas/linux/proc_net_dev.h @@ -0,0 +1,100 @@ +/* + * Linux /proc/net/dev metrics cluster + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 1995,2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +typedef struct { + uint32_t mtu; + uint32_t speed; + uint8_t duplex; + uint8_t linkup; + uint8_t running; + uint8_t pad; +} net_dev_t; + +#define HWADDRSTRLEN 64 + +typedef struct { + int has_inet : 1; + int has_ipv6 : 1; + int has_hw : 1; + int padding : 13; + uint16_t ipv6scope; + char inet[INET_ADDRSTRLEN]; + char ipv6[INET6_ADDRSTRLEN+16]; /* extra for /plen */ + char hw_addr[HWADDRSTRLEN]; +} net_addr_t; + +#define PROC_DEV_COUNTERS_PER_LINE 16 + +typedef struct { + uint64_t last_gen; + uint64_t last_counters[PROC_DEV_COUNTERS_PER_LINE]; + uint64_t counters[PROC_DEV_COUNTERS_PER_LINE]; + net_dev_t ioc; +} net_interface_t; + +#ifndef ETHTOOL_GSET +#define ETHTOOL_GSET 0x1 +#endif + +#ifndef SIOCGIFCONF +#define SIOCGIFCONF 0x8912 +#endif + +#ifndef SIOCGIFFLAGS +#define SIOCGIFFLAGS 0x8913 +#endif + +#ifndef SIOCGIFADDR +#define SIOCGIFADDR 0x8915 +#endif + +#ifndef SIOCGIFMTU +#define SIOCGIFMTU 0x8921 +#endif + +#ifndef SIOCETHTOOL +#define SIOCETHTOOL 0x8946 +#endif + +/* ioctl(SIOCIFETHTOOL) GSET ("get settings") structure */ +struct ethtool_cmd { + uint32_t cmd; + uint32_t supported; /* Features this interface supports */ + uint32_t advertising; /* Features this interface advertises */ + uint16_t speed; /* The forced speed, 10Mb, 100Mb, gigabit */ + uint8_t duplex; /* Duplex, half or full */ + uint8_t port; /* Which connector port */ + uint8_t phy_address; + uint8_t transceiver; /* Which tranceiver to use */ + uint8_t autoneg; /* Enable or disable autonegotiation */ + uint32_t maxtxpkt; /* Tx pkts before generating tx int */ + uint32_t maxrxpkt; /* Rx pkts before generating rx int */ + uint32_t reserved[4]; +}; + +#define IPV6_ADDR_ANY 0x0000U +#define IPV6_ADDR_UNICAST 0x0001U +#define IPV6_ADDR_MULTICAST 0x0002U +#define IPV6_ADDR_ANYCAST 0x0004U +#define IPV6_ADDR_LOOPBACK 0x0010U +#define IPV6_ADDR_LINKLOCAL 0x0020U +#define IPV6_ADDR_SITELOCAL 0x0040U +#define IPV6_ADDR_COMPATv4 0x0080U + +extern int refresh_proc_net_dev(pmInDom); +extern int refresh_net_dev_addr(pmInDom); +extern char *lookup_ipv6_scope(int); diff --git a/src/pmdas/linux/proc_net_netstat.c b/src/pmdas/linux/proc_net_netstat.c new file mode 100644 index 0000000..a7bd34a --- /dev/null +++ b/src/pmdas/linux/proc_net_netstat.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "proc_net_netstat.h" + +extern proc_net_netstat_t _pm_proc_net_netstat; + +typedef struct { + const char *field; + __uint64_t *offset; +} netstat_fields_t; + +netstat_fields_t netstat_ip_fields[] = { + { .field = "InNoRoutes", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INNOROUTES] }, + { .field = "InTruncatedPkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INTRUNCATEDPKTS] }, + { .field = "InMcastPkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INMCASTPKTS] }, + { .field = "OutMcastPkts ", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTMCASTPKTS] }, + { .field = "InBcastPkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INBCASTPKTS] }, + { .field = "OutBcastPkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTBCASTPKTS] }, + { .field = "InOctets", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INOCTETS] }, + { .field = "OutOctets", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTOCTETS] }, + { .field = "InMcastOctets", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INMCASTOCTETS] }, + { .field = "OutMcastOctets", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTMCASTOCTETS] }, + { .field = "InBcastOctets", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_INBCASTOCTETS] }, + { .field = "OutBcastOctets", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_OUTBCASTOCTETS] }, + { .field = "InCsumErrors", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_CSUMERRORS] }, + { .field = "InNoECTPkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_NOECTPKTS] }, + { .field = "InECT1Pkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_ECT1PKTS] }, + { .field = "InECT0Pkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_ECT0PKTS] }, + { .field = "InCEPkts", + .offset = &_pm_proc_net_netstat.ip[_PM_NETSTAT_IPEXT_CEPKTS] } +}; + + +netstat_fields_t netstat_tcp_fields[] = { + { .field = "SyncookiesSent", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESSENT] }, + { .field = "SyncookiesRecv", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESRECV] }, + { .field = "SyncookiesFailed", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SYNCOOKIESFAILED] }, + { .field = "EmbryonicRsts", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_EMBRYONICRSTS] }, + { .field = "PruneCalled", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PRUNECALLED] }, + { .field = "RcvPruned", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_RCVPRUNED] }, + { .field = "OfoPruned", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_OFOPRUNED] }, + { .field = "OutOfWindowIcmps", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_OUTOFWINDOWICMPS] }, + { .field = "LockDroppedIcmps", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LOCKDROPPEDICMPS] }, + { .field = "ArpFilter", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_ARPFILTER] }, + { .field = "TW", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITED] }, + { .field = "TWRecycled", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITRECYCLED] }, + { .field = "TWKilled", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TIMEWAITKILLED] }, + { .field = "PAWSPassive", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSPASSIVEREJECTED] }, + { .field = "PAWSActive", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSACTIVEREJECTED] }, + { .field = "PAWSEstab", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_PAWSESTABREJECTED] }, + { .field = "DelayedACKs", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKS] }, + { .field = "DelayedACKLocked", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKLOCKED] }, + { .field = "DelayedACKLost", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_DELAYEDACKLOST] }, + { .field = "ListenOverflows", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LISTENOVERFLOWS] }, + { .field = "ListenDrops", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_LISTENDROPS] }, + { .field = "TCPPrequeued", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPREQUEUED] }, + { .field = "TCPDirectCopyFromBacklog", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMBACKLOG] }, + { .field = "TCPDirectCopyFromPrequeue", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMPREQUEUE] }, + { .field = "TCPPrequeueDropped", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPREQUEUEDROPPED] }, + { .field = "TCPHPHits", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPHITS] }, + { .field = "TCPHPHitsToUser", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPHITSTOUSER] }, + { .field = "TCPPureAcks", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPUREACKS] }, + { .field = "TCPHPAcks", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPHPACKS] }, + { .field = "TCPRenoRecovery", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENORECOVERY] }, + { .field = "TCPSackRecovery", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRECOVERY] }, + { .field = "TCPSACKReneging", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRENEGING] }, + { .field = "TCPFACKReorder", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFACKREORDER] }, + { .field = "TCPSACKReorder", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKREORDER] }, + { .field = "TCPRenoReorder", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENOREORDER] }, + { .field = "TCPTSReorder", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTSREORDER] }, + { .field = "TCPFullUndo", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFULLUNDO] }, + { .field = "TCPPartialUndo", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPPARTIALUNDO] }, + { .field = "TCPDSACKUndo", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKUNDO] }, + { .field = "TCPLossUndo", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSUNDO] }, + { .field = "TCPLostRetransmit", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSTRETRANSMIT] }, + { .field = "TCPRenoFailures", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENOFAILURES] }, + { .field = "TCPSackFailures", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKFAILURES] }, + { .field = "TCPLossFailures", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSFAILURES] }, + { .field = "TCPFastRetrans", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTRETRANS] }, + { .field = "TCPForwardRetrans", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFORWARDRETRANS] }, + { .field = "TCPSlowStartRetrans", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSLOWSTARTRETRANS] }, + { .field = "TCPTimeouts", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTIMEOUTS] }, + { .field = "TCPLossProbes", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSPROBES] }, + { .field = "TCPLossProbeRecovery", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPLOSSPROBERECOVERY] }, + { .field = "TCPRenoRecoveryFail", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRENORECOVERYFAIL] }, + { .field = "TCPSackRecoveryFail", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKRECOVERYFAIL] }, + { .field = "TCPSchedulerFailed", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSCHEDULERFAILED] }, + { .field = "TCPRcvCollapsed", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRCVCOLLAPSED] }, + { .field = "TCPDSACKOldSent", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOLDSENT] }, + { .field = "TCPDSACKOfoSent", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOFOSENT] }, + { .field = "TCPDSACKRecv", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKRECV] }, + { .field = "TCPDSACKOfoRecv", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKOFORECV] }, + { .field = "TCPAbortOnData", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONDATA] }, + { .field = "TCPAbortOnClose", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONCLOSE] }, + { .field = "TCPAbortOnMemory", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONMEMORY] }, + { .field = "TCPAbortOnTimeout", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONTIMEOUT] }, + { .field = "TCPAbortOnLinger", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTONLINGER] }, + { .field = "TCPAbortFailed", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPABORTFAILED] }, + { .field = "TCPMemoryPressures", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMEMORYPRESSURES] }, + { .field = "TCPSACKDiscard", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSACKDISCARD] }, + { .field = "TCPDSACKIgnoredOld", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDOLD] }, + { .field = "TCPDSACKIgnoredNoUndo", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDNOUNDO] }, + { .field = "TCPSpuriousRTOs", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSPURIOUSRTOS] }, + { .field = "TCPMD5NotFound", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMD5NOTFOUND] }, + { .field = "TCPMD5Unexpected", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMD5UNEXPECTED] }, + { .field = "TCPSackShifted", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKSHIFTED] }, + { .field = "TCPSackMerged", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKMERGED] }, + { .field = "TCPSackShiftFallback", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_SACKSHIFTFALLBACK] }, + { .field = "TCPBacklogDrop", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPBACKLOGDROP] }, + { .field = "TCPMinTTLDrop", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPMINTTLDROP] }, + { .field = "TCPDeferAcceptDrop", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPDEFERACCEPTDROP] }, + { .field = "IPReversePathFilter", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_IPRPFILTER] }, + { .field = "TCPTimeWaitOverflow", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTIMEWAITOVERFLOW] }, + { .field = "TCPReqQFullDoCookies", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPREQQFULLDOCOOKIES] }, + { .field = "TCPReqQFullDrop", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPREQQFULLDROP] }, + { .field = "TCPRetransFail", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRETRANSFAIL] }, + { .field = "TCPRcvCoalesce", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPRCVCOALESCE] }, + { .field = "TCPOFOQueue", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFOQUEUE] }, + { .field = "TCPOFODrop", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFODROP] }, + { .field = "TCPOFOMerge", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPOFOMERGE] }, + { .field = "TCPChallengeACK", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPCHALLENGEACK] }, + { .field = "TCPSYNChallenge", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSYNCHALLENGE] }, + { .field = "TCPFastOpenActive", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVE] }, + { .field = "TCPFastOpenPassive", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVEFAIL] }, + { .field = "TCPFastOpenPassiveFail", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENPASSIVEFAIL] }, + { .field = "TCPFastOpenListenOverflow", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENLISTENOVERFLOW] }, + { .field = "TCPFastOpenCookieReqd", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFASTOPENCOOKIEREQD] }, + { .field = "TCPSpuriousRtxHostQueues", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSPURIOUS_RTX_HOSTQUEUES] }, + { .field = "BusyPollRxPackets", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_BUSYPOLLRXPACKETS] }, + { .field = "TCPAutoCorking", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPAUTOCORKING] }, + { .field = "TCPFromZeroWindowAdv", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPFROMZEROWINDOWADV] }, + { .field = "TCPToZeroWindowAdv", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPTOZEROWINDOWADV] }, + { .field = "TCPWantZeroWindowAdv", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPWANTZEROWINDOWADV] }, + { .field = "TCPSynRetrans", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPSYNRETRANS] }, + { .field = "TCPOrigDataSent", + .offset = &_pm_proc_net_netstat.tcp[_PM_NETSTAT_TCPEXT_TCPORIGDATASENT] } +}; + +static void +get_fields(netstat_fields_t *fields, char *header, char *buffer) +{ + int i, j, count; + char *p, *indices[NETSTAT_MAX_COLUMNS]; + + /* first get pointers to each of the column headings */ + strtok(header, " "); + for (i = 0; i < NETSTAT_MAX_COLUMNS; i++) { + if ((p = strtok(NULL, " \n")) == NULL) + break; + indices[i] = p; + } + count = i; + + /* + * Extract values via back-referencing column headings. + * "i" is the last found index, which we use for a bit + * of optimisation for the (common) in-order maps case + * (where "in order" means in the order defined by the + * passed in "fields" table which typically matches the + * kernel - but may be out-of-order for older kernels). + */ + strtok(buffer, " "); + for (i = j = 0; j < count && fields[i].field; j++, i++) { + if ((p = strtok(NULL, " \n")) == NULL) + break; + if (strcmp(fields[i].field, indices[j]) == 0) + *fields[i].offset = strtoull(p, NULL, 10); + else { + for (i = 0; fields[i].field; i++) { + if (strcmp(fields[i].field, indices[j]) != 0) + continue; + *fields[i].offset = strtoull(p, NULL, 10); + break; + } + if (fields[i].field == NULL) /* not found, ignore */ + i = 0; + } + } +} + + +#define NETSTAT_IP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)netstat_ip_fields[ii].offset - (__psint_t)&_pm_proc_net_netstat.ip) +#define NETSTAT_TCP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)netstat_tcp_fields[ii].offset - (__psint_t)&_pm_proc_net_netstat.tcp) + +static void +init_refresh_proc_net_netstat(proc_net_netstat_t *netstat) +{ + int i; + + /* initially, all marked as "no value available" */ + for (i = 0; netstat_ip_fields[i].field != NULL; i++) + *(NETSTAT_IP_OFFSET(i, netstat->ip)) = -1; + for (i = 0; netstat_tcp_fields[i].field != NULL; i++) + *(NETSTAT_TCP_OFFSET(i, netstat->tcp)) = -1; +} + +int +refresh_proc_net_netstat(proc_net_netstat_t *netstat) +{ + /* Need a sufficiently large value to hold a full line */ + char buf[MAXPATHLEN]; + char header[2048]; + FILE *fp; + + init_refresh_proc_net_netstat(netstat); + if ((fp = linux_statsfile("/proc/net/netstat", buf, sizeof(buf))) == NULL) + return -oserror(); + while (fgets(header, sizeof(header), fp) != NULL) { + if (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "IpExt:", 6) == 0) + get_fields(netstat_ip_fields, header, buf); + else if (strncmp(buf, "TcpExt:", 7) == 0) + get_fields(netstat_tcp_fields, header, buf); + else + __pmNotifyErr(LOG_ERR, "Unrecognised netstat row: %s\n", buf); + } + } + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/proc_net_netstat.h b/src/pmdas/linux/proc_net_netstat.h new file mode 100644 index 0000000..4a5a9c8 --- /dev/null +++ b/src/pmdas/linux/proc_net_netstat.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define NETSTAT_MAX_COLUMNS 256 /* arbitrary upper bound (228 observed as of 22/04/2014)*/ + +enum { + _PM_NETSTAT_IPEXT_INNOROUTES = 0, + _PM_NETSTAT_IPEXT_INTRUNCATEDPKTS, + _PM_NETSTAT_IPEXT_INMCASTPKTS, + _PM_NETSTAT_IPEXT_OUTMCASTPKTS, + _PM_NETSTAT_IPEXT_INBCASTPKTS, + _PM_NETSTAT_IPEXT_OUTBCASTPKTS, + _PM_NETSTAT_IPEXT_INOCTETS, + _PM_NETSTAT_IPEXT_OUTOCTETS, + _PM_NETSTAT_IPEXT_INMCASTOCTETS, + _PM_NETSTAT_IPEXT_OUTMCASTOCTETS, + _PM_NETSTAT_IPEXT_INBCASTOCTETS, + _PM_NETSTAT_IPEXT_OUTBCASTOCTETS, + _PM_NETSTAT_IPEXT_CSUMERRORS, + _PM_NETSTAT_IPEXT_NOECTPKTS, + _PM_NETSTAT_IPEXT_ECT1PKTS, + _PM_NETSTAT_IPEXT_ECT0PKTS, + _PM_NETSTAT_IPEXT_CEPKTS, + _PM_NETSTAT_IPEXT_NFIELDS /* must be last */ +}; + +enum { + _PM_NETSTAT_TCPEXT_SYNCOOKIESSENT = 0, + _PM_NETSTAT_TCPEXT_SYNCOOKIESRECV, + _PM_NETSTAT_TCPEXT_SYNCOOKIESFAILED, + _PM_NETSTAT_TCPEXT_EMBRYONICRSTS, + _PM_NETSTAT_TCPEXT_PRUNECALLED, + _PM_NETSTAT_TCPEXT_RCVPRUNED, + _PM_NETSTAT_TCPEXT_OFOPRUNED, + _PM_NETSTAT_TCPEXT_OUTOFWINDOWICMPS, + _PM_NETSTAT_TCPEXT_LOCKDROPPEDICMPS, + _PM_NETSTAT_TCPEXT_ARPFILTER, + _PM_NETSTAT_TCPEXT_TIMEWAITED, + _PM_NETSTAT_TCPEXT_TIMEWAITRECYCLED, + _PM_NETSTAT_TCPEXT_TIMEWAITKILLED, + _PM_NETSTAT_TCPEXT_PAWSPASSIVEREJECTED, + _PM_NETSTAT_TCPEXT_PAWSACTIVEREJECTED, + _PM_NETSTAT_TCPEXT_PAWSESTABREJECTED, + _PM_NETSTAT_TCPEXT_DELAYEDACKS, + _PM_NETSTAT_TCPEXT_DELAYEDACKLOCKED, + _PM_NETSTAT_TCPEXT_DELAYEDACKLOST, + _PM_NETSTAT_TCPEXT_LISTENOVERFLOWS, + _PM_NETSTAT_TCPEXT_LISTENDROPS, + _PM_NETSTAT_TCPEXT_TCPPREQUEUED, + _PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMBACKLOG, + _PM_NETSTAT_TCPEXT_TCPDIRECTCOPYFROMPREQUEUE, + _PM_NETSTAT_TCPEXT_TCPPREQUEUEDROPPED, + _PM_NETSTAT_TCPEXT_TCPHPHITS, + _PM_NETSTAT_TCPEXT_TCPHPHITSTOUSER, + _PM_NETSTAT_TCPEXT_TCPPUREACKS, + _PM_NETSTAT_TCPEXT_TCPHPACKS, + _PM_NETSTAT_TCPEXT_TCPRENORECOVERY, + _PM_NETSTAT_TCPEXT_TCPSACKRECOVERY, + _PM_NETSTAT_TCPEXT_TCPSACKRENEGING, + _PM_NETSTAT_TCPEXT_TCPFACKREORDER, + _PM_NETSTAT_TCPEXT_TCPSACKREORDER, + _PM_NETSTAT_TCPEXT_TCPRENOREORDER, + _PM_NETSTAT_TCPEXT_TCPTSREORDER, + _PM_NETSTAT_TCPEXT_TCPFULLUNDO, + _PM_NETSTAT_TCPEXT_TCPPARTIALUNDO, + _PM_NETSTAT_TCPEXT_TCPDSACKUNDO, + _PM_NETSTAT_TCPEXT_TCPLOSSUNDO, + _PM_NETSTAT_TCPEXT_TCPLOSTRETRANSMIT, + _PM_NETSTAT_TCPEXT_TCPRENOFAILURES, + _PM_NETSTAT_TCPEXT_TCPSACKFAILURES, + _PM_NETSTAT_TCPEXT_TCPLOSSFAILURES, + _PM_NETSTAT_TCPEXT_TCPFASTRETRANS, + _PM_NETSTAT_TCPEXT_TCPFORWARDRETRANS, + _PM_NETSTAT_TCPEXT_TCPSLOWSTARTRETRANS, + _PM_NETSTAT_TCPEXT_TCPTIMEOUTS, + _PM_NETSTAT_TCPEXT_TCPLOSSPROBES, + _PM_NETSTAT_TCPEXT_TCPLOSSPROBERECOVERY, + _PM_NETSTAT_TCPEXT_TCPRENORECOVERYFAIL, + _PM_NETSTAT_TCPEXT_TCPSACKRECOVERYFAIL, + _PM_NETSTAT_TCPEXT_TCPSCHEDULERFAILED, + _PM_NETSTAT_TCPEXT_TCPRCVCOLLAPSED, + _PM_NETSTAT_TCPEXT_TCPDSACKOLDSENT, + _PM_NETSTAT_TCPEXT_TCPDSACKOFOSENT, + _PM_NETSTAT_TCPEXT_TCPDSACKRECV, + _PM_NETSTAT_TCPEXT_TCPDSACKOFORECV, + _PM_NETSTAT_TCPEXT_TCPABORTONDATA, + _PM_NETSTAT_TCPEXT_TCPABORTONCLOSE, + _PM_NETSTAT_TCPEXT_TCPABORTONMEMORY, + _PM_NETSTAT_TCPEXT_TCPABORTONTIMEOUT, + _PM_NETSTAT_TCPEXT_TCPABORTONLINGER, + _PM_NETSTAT_TCPEXT_TCPABORTFAILED, + _PM_NETSTAT_TCPEXT_TCPMEMORYPRESSURES, + _PM_NETSTAT_TCPEXT_TCPSACKDISCARD, + _PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDOLD, + _PM_NETSTAT_TCPEXT_TCPDSACKIGNOREDNOUNDO, + _PM_NETSTAT_TCPEXT_TCPSPURIOUSRTOS, + _PM_NETSTAT_TCPEXT_TCPMD5NOTFOUND, + _PM_NETSTAT_TCPEXT_TCPMD5UNEXPECTED, + _PM_NETSTAT_TCPEXT_SACKSHIFTED, + _PM_NETSTAT_TCPEXT_SACKMERGED, + _PM_NETSTAT_TCPEXT_SACKSHIFTFALLBACK, + _PM_NETSTAT_TCPEXT_TCPBACKLOGDROP, + _PM_NETSTAT_TCPEXT_TCPMINTTLDROP, + _PM_NETSTAT_TCPEXT_TCPDEFERACCEPTDROP, + _PM_NETSTAT_TCPEXT_IPRPFILTER, + _PM_NETSTAT_TCPEXT_TCPTIMEWAITOVERFLOW, + _PM_NETSTAT_TCPEXT_TCPREQQFULLDOCOOKIES, + _PM_NETSTAT_TCPEXT_TCPREQQFULLDROP, + _PM_NETSTAT_TCPEXT_TCPRETRANSFAIL, + _PM_NETSTAT_TCPEXT_TCPRCVCOALESCE, + _PM_NETSTAT_TCPEXT_TCPOFOQUEUE, + _PM_NETSTAT_TCPEXT_TCPOFODROP, + _PM_NETSTAT_TCPEXT_TCPOFOMERGE, + _PM_NETSTAT_TCPEXT_TCPCHALLENGEACK, + _PM_NETSTAT_TCPEXT_TCPSYNCHALLENGE, + _PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVE, + _PM_NETSTAT_TCPEXT_TCPFASTOPENACTIVEFAIL, + _PM_NETSTAT_TCPEXT_TCPFASTOPENPASSIVE, + _PM_NETSTAT_TCPEXT_TCPFASTOPENPASSIVEFAIL, + _PM_NETSTAT_TCPEXT_TCPFASTOPENLISTENOVERFLOW, + _PM_NETSTAT_TCPEXT_TCPFASTOPENCOOKIEREQD, + _PM_NETSTAT_TCPEXT_TCPSPURIOUS_RTX_HOSTQUEUES, + _PM_NETSTAT_TCPEXT_BUSYPOLLRXPACKETS, + _PM_NETSTAT_TCPEXT_TCPAUTOCORKING, + _PM_NETSTAT_TCPEXT_TCPFROMZEROWINDOWADV, + _PM_NETSTAT_TCPEXT_TCPTOZEROWINDOWADV, + _PM_NETSTAT_TCPEXT_TCPWANTZEROWINDOWADV, + _PM_NETSTAT_TCPEXT_TCPSYNRETRANS, + _PM_NETSTAT_TCPEXT_TCPORIGDATASENT, + _PM_NETSTAT_TCPEXT_NFIELDS /* must be last */ +}; + + +typedef struct { + __uint64_t ip[_PM_NETSTAT_IPEXT_NFIELDS]; + __uint64_t tcp[_PM_NETSTAT_TCPEXT_NFIELDS]; +} proc_net_netstat_t; + +extern int refresh_proc_net_netstat(proc_net_netstat_t *); diff --git a/src/pmdas/linux/proc_net_rpc.c b/src/pmdas/linux/proc_net_rpc.c new file mode 100644 index 0000000..1b1a940 --- /dev/null +++ b/src/pmdas/linux/proc_net_rpc.c @@ -0,0 +1,188 @@ +/* + * Linux /proc/net/rpc metrics cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include "proc_net_rpc.h" + +int +refresh_proc_net_rpc(proc_net_rpc_t *proc_net_rpc) +{ + char buf[4096]; + FILE *fp; + char *p; + int i; + + memset(proc_net_rpc, 0, sizeof(proc_net_rpc_t)); + + /* + * client stats + */ + if ((fp = linux_statsfile("/proc/net/rpc/nfs", buf, sizeof(buf))) == NULL) { + proc_net_rpc->client.errcode = -oserror(); + } + else { + proc_net_rpc->client.errcode = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "net", 3) == 0) + sscanf(buf, "net %u %u %u %u", + &proc_net_rpc->client.netcnt, + &proc_net_rpc->client.netudpcnt, + &proc_net_rpc->client.nettcpcnt, + &proc_net_rpc->client.nettcpconn); + else + if (strncmp(buf, "rpc", 3) == 0) + sscanf(buf, "rpc %u %u %u", + &proc_net_rpc->client.rpccnt, + &proc_net_rpc->client.rpcretrans, + &proc_net_rpc->client.rpcauthrefresh); + else + if (strncmp(buf, "proc2", 5) == 0) { + if ((p = strtok(buf, " ")) != NULL) + p = strtok(NULL, " "); + for (i=0; p && i < NR_RPC_COUNTERS; i++) { + if ((p = strtok(NULL, " ")) == NULL) + break; + proc_net_rpc->client.reqcounts[i] = strtoul(p, (char **)NULL, 10); + } + } + else + if (strncmp(buf, "proc3", 5) == 0) { + if ((p = strtok(buf, " ")) != NULL) + p = strtok(NULL, " "); + for (i=0; p && i < NR_RPC3_COUNTERS; i++) { + if ((p = strtok(NULL, " ")) == NULL) + break; + proc_net_rpc->client.reqcounts3[i] = strtoul(p, (char **)NULL, 10); + } + } + else + if (strncmp(buf, "proc4", 5) == 0) { + if ((p = strtok(buf, " ")) != NULL) + p = strtok(NULL, " "); + for (i=0; p && i < NR_RPC4_CLI_COUNTERS; i++) { + if ((p = strtok(NULL, " ")) == NULL) + break; + proc_net_rpc->client.reqcounts4[i] = strtoul(p, (char **)NULL, 10); + } + } + } + + fclose(fp); + } + + /* + * server stats + */ + if ((fp = linux_statsfile("/proc/net/rpc/nfsd", buf, sizeof(buf))) == NULL) { + proc_net_rpc->server.errcode = -oserror(); + } + else { + proc_net_rpc->server.errcode = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "rc", 2) == 0) + sscanf(buf, "rc %u %u %u %u %u %u %u %u %u", + &proc_net_rpc->server.rchits, + &proc_net_rpc->server.rcmisses, + &proc_net_rpc->server.rcnocache, + &proc_net_rpc->server.fh_cached, + &proc_net_rpc->server.fh_valid, + &proc_net_rpc->server.fh_fixup, + &proc_net_rpc->server.fh_lookup, + &proc_net_rpc->server.fh_stale, + &proc_net_rpc->server.fh_concurrent); + else + if (strncmp(buf, "fh", 2) == 0) + sscanf(buf, "fh %u %u %u %u %u", + &proc_net_rpc->server.fh_stale, + &proc_net_rpc->server.fh_lookup, + &proc_net_rpc->server.fh_anon, + &proc_net_rpc->server.fh_nocache_dir, + &proc_net_rpc->server.fh_nocache_nondir); + else + if (strncmp(buf, "io", 2) == 0) + sscanf(buf, "io %u %u", + &proc_net_rpc->server.io_read, + &proc_net_rpc->server.io_write); + else + if (strncmp(buf, "th", 2) == 0) + sscanf(buf, "th %u %u", + &proc_net_rpc->server.th_cnt, + &proc_net_rpc->server.th_fullcnt); + else + if (strncmp(buf, "net", 3) == 0) + sscanf(buf, "net %u %u %u %u", + &proc_net_rpc->server.netcnt, + &proc_net_rpc->server.netudpcnt, + &proc_net_rpc->server.nettcpcnt, + &proc_net_rpc->server.nettcpconn); + else + if (strncmp(buf, "rpc", 3) == 0) + sscanf(buf, "rpc %u %u %u", + &proc_net_rpc->server.rpccnt, + &proc_net_rpc->server.rpcerr, /* always the sum of the following three fields */ + &proc_net_rpc->server.rpcbadfmt); + else + if (strncmp(buf, "proc2", 5) == 0) { + if ((p = strtok(buf, " ")) != NULL) + p = strtok(NULL, " "); + for (i=0; p && i < NR_RPC_COUNTERS; i++) { + if ((p = strtok(NULL, " ")) == NULL) + break; + proc_net_rpc->server.reqcounts[i] = strtoul(p, (char **)NULL, 10); + } + } + else + if (strncmp(buf, "proc3", 5) == 0) { + if ((p = strtok(buf, " ")) != NULL) + p = strtok(NULL, " "); + for (i=0; p && i < NR_RPC3_COUNTERS; i++) { + if ((p = strtok(NULL, " ")) == NULL) + break; + proc_net_rpc->server.reqcounts3[i] = strtoul(p, (char **)NULL, 10); + } + } + else + if (strncmp(buf, "proc4ops", 8) == 0) { + if ((p = strtok(buf, " ")) != NULL) + p = strtok(NULL, " "); + + /* Inst 0 is NULL count (below) */ + for (i=1; p && i < NR_RPC4_SVR_COUNTERS; i++) { + if ((p = strtok(NULL, " ")) == NULL) + break; + proc_net_rpc->server.reqcounts4[i] = strtoul(p, (char **)NULL, 10); + } + } + else + if (strncmp(buf, "proc4", 5) == 0) { + if ((strtok(buf, " ")) != NULL && + (strtok(NULL, " ")) != NULL && + (p = strtok(NULL, " ")) != NULL) { /* 3rd token is NULL count */ + proc_net_rpc->server.reqcounts4[0] = strtoul(p, (char **)NULL, 10); + } + } + } + + fclose(fp); + } + + if (proc_net_rpc->client.errcode == 0 && proc_net_rpc->server.errcode == 0) + return 0; + return -1; +} diff --git a/src/pmdas/linux/proc_net_rpc.h b/src/pmdas/linux/proc_net_rpc.h new file mode 100644 index 0000000..880dbed --- /dev/null +++ b/src/pmdas/linux/proc_net_rpc.h @@ -0,0 +1,99 @@ +/* + * Linux /proc/net/rpc metrics cluster + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define NR_RPC_COUNTERS 18 +#define NR_RPC3_COUNTERS 22 +#define NR_RPC4_CLI_COUNTERS 35 +#define NR_RPC4_SVR_COUNTERS 41 + +typedef struct { + struct { + int errcode; /* error from last refresh */ + /* /proc/net/rpc/nfs "net" */ + unsigned int netcnt; + unsigned int netudpcnt; + unsigned int nettcpcnt; + unsigned int nettcpconn; + + /* /proc/net/rpc/nfs "rpc" */ + unsigned int rpccnt; + unsigned int rpcretrans; + unsigned int rpcauthrefresh; + + /* /proc/net/rpc/nfs "proc2" */ + unsigned int reqcounts[NR_RPC_COUNTERS]; + + /* /proc/net/rpc/nfs "proc3" */ + unsigned int reqcounts3[NR_RPC3_COUNTERS]; + + /* /proc/net/rpc/nfs "proc4" */ + unsigned int reqcounts4[NR_RPC4_CLI_COUNTERS]; + } client; + + struct { + int errcode; /* error from last refresh */ + /* /proc/net/rpc/nfsd "rc" and "fh" */ + unsigned int rchits; /* repcache hits */ + unsigned int rcmisses; /* repcache hits */ + unsigned int rcnocache; /* uncached reqs */ + unsigned int fh_cached; /* dentry cached */ + unsigned int fh_valid; /* dentry validated */ + unsigned int fh_fixup; /* dentry fixup validated */ + unsigned int fh_lookup; /* new lookup required */ + unsigned int fh_stale; /* FH stale error */ + unsigned int fh_concurrent; /* concurrent request */ + unsigned int fh_anon; /* anon file dentry returned */ + unsigned int fh_nocache_dir; /* dir filehandle not found in dcache */ + unsigned int fh_nocache_nondir; /* nondir filehandle not in dcache */ + + /* /proc/net/rpc/nfsd "io" */ + unsigned int io_read; /* bytes returned to read requests */ + unsigned int io_write; /* bytes passed in write requests */ + + /* /proc/net/rpc/nfsd "th" */ + unsigned int th_cnt; /* available nfsd threads */ + unsigned int th_fullcnt; /* times last free thread used */ + + /* /proc/net/rpc/nfsd "net" */ + unsigned int netcnt; + unsigned int netudpcnt; + unsigned int nettcpcnt; + unsigned int nettcpconn; + + /* /proc/net/rpc/nfsd "rpc" */ + unsigned int rpccnt; + unsigned int rpcerr; + unsigned int rpcbadfmt; + unsigned int rpcbadauth; + unsigned int rpcbadclnt; + + /* /proc/net/rpc/nfsd "proc2" */ + unsigned int reqcounts[NR_RPC_COUNTERS]; + + /* /proc/net/rpc/nfsd "proc3" */ + unsigned int reqcounts3[NR_RPC3_COUNTERS]; + + /* /proc/net/rpc/nfsd "proc4" & "proc4ops" */ + unsigned int reqcounts4[NR_RPC4_SVR_COUNTERS]; + } server; + +} proc_net_rpc_t; + +extern int refresh_proc_net_rpc(proc_net_rpc_t *); diff --git a/src/pmdas/linux/proc_net_snmp.c b/src/pmdas/linux/proc_net_snmp.c new file mode 100644 index 0000000..431d984 --- /dev/null +++ b/src/pmdas/linux/proc_net_snmp.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "proc_net_snmp.h" + +extern proc_net_snmp_t _pm_proc_net_snmp; +extern pmdaInstid _pm_proc_net_snmp_indom_id[]; +static char *proc_net_snmp_icmpmsg_names; + +typedef struct { + const char *field; + __uint64_t *offset; +} snmp_fields_t; + +snmp_fields_t ip_fields[] = { + { .field = "Forwarding", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FORWARDING] }, + { .field = "DefaultTTL", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_DEFAULTTTL] }, + { .field = "InReceives", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INRECEIVES] }, + { .field = "InHdrErrors", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INHDRERRORS] }, + { .field = "InAddrErrors", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INADDRERRORS] }, + { .field = "ForwDatagrams", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FORWDATAGRAMS] }, + { .field = "InUnknownProtos", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INUNKNOWNPROTOS] }, + { .field = "InDiscards", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INDISCARDS] }, + { .field = "InDelivers", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_INDELIVERS] }, + { .field = "OutRequests", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTREQUESTS] }, + { .field = "OutDiscards", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTDISCARDS] }, + { .field = "OutNoRoutes", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_OUTNOROUTES] }, + { .field = "ReasmTimeout", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMTIMEOUT] }, + { .field = "ReasmReqds", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMREQDS] }, + { .field = "ReasmOKs", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMOKS] }, + { .field = "ReasmFails", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_REASMFAILS] }, + { .field = "FragOKs", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGOKS] }, + { .field = "FragFails", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGFAILS] }, + { .field = "FragCreates", + .offset = &_pm_proc_net_snmp.ip[_PM_SNMP_IP_FRAGCREATES] }, + { .field = NULL, .offset = NULL } +}; + +snmp_fields_t icmp_fields[] = { + { .field = "InMsgs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INMSGS] }, + { .field = "InErrors", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INERRORS] }, + { .field = "InCsumErrors", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INCSUMERRORS] }, + { .field = "InDestUnreachs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INDESTUNREACHS] }, + { .field = "InTimeExcds", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMEEXCDS] }, + { .field = "InParmProbs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INPARMPROBS] }, + { .field = "InSrcQuenchs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INSRCQUENCHS] }, + { .field = "InRedirects", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INREDIRECTS] }, + { .field = "InEchos", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INECHOS] }, + { .field = "InEchoReps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INECHOREPS] }, + { .field = "InTimestamps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMESTAMPS] }, + { .field = "InTimestampReps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INTIMESTAMPREPS] }, + { .field = "InAddrMasks", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INADDRMASKS] }, + { .field = "InAddrMaskReps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_INADDRMASKREPS] }, + { .field = "OutMsgs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTMSGS] }, + { .field = "OutErrors", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTERRORS] }, + { .field = "OutDestUnreachs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTDESTUNREACHS] }, + { .field = "OutTimeExcds", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMEEXCDS] }, + { .field = "OutParmProbs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTPARMPROBS] }, + { .field = "OutSrcQuenchs", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTSRCQUENCHS] }, + { .field = "OutRedirects", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTREDIRECTS] }, + { .field = "OutEchos", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTECHOS] }, + { .field = "OutEchoReps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTECHOREPS] }, + { .field = "OutTimestamps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMESTAMPS] }, + { .field = "OutTimestampReps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTTIMESTAMPREPS] }, + { .field = "OutAddrMasks", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTADDRMASKS] }, + { .field = "OutAddrMaskReps", + .offset = &_pm_proc_net_snmp.icmp[_PM_SNMP_ICMP_OUTADDRMASKREPS] }, + { .field = NULL, .offset = NULL } +}; + +snmp_fields_t icmpmsg_fields[] = { + { .field = "InType%u", + .offset = &_pm_proc_net_snmp.icmpmsg[_PM_SNMP_ICMPMSG_INTYPE] }, + { .field = "OutType%u", + .offset = &_pm_proc_net_snmp.icmpmsg[_PM_SNMP_ICMPMSG_OUTTYPE] }, + { .field = NULL, .offset = NULL } +}; + +snmp_fields_t tcp_fields[] = { + { .field = "RtoAlgorithm", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOALGORITHM] }, + { .field = "RtoMin", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOMIN] }, + { .field = "RtoMax", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RTOMAX] }, + { .field = "MaxConn", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_MAXCONN] }, + { .field = "ActiveOpens", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ACTIVEOPENS] }, + { .field = "PassiveOpens", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_PASSIVEOPENS] }, + { .field = "AttemptFails", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ATTEMPTFAILS] }, + { .field = "EstabResets", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_ESTABRESETS] }, + { .field = "CurrEstab", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_CURRESTAB] }, + { .field = "InSegs", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INSEGS] }, + { .field = "OutSegs", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_OUTSEGS] }, + { .field = "RetransSegs", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_RETRANSSEGS] }, + { .field = "InErrs", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INERRS] }, + { .field = "OutRsts", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_OUTRSTS] }, + { .field = "InCsumErrors", + .offset = &_pm_proc_net_snmp.tcp[_PM_SNMP_TCP_INCSUMERRORS] }, + { .field = NULL, .offset = NULL } +}; + +snmp_fields_t udp_fields[] = { + { .field = "InDatagrams", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INDATAGRAMS] }, + { .field = "NoPorts", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_NOPORTS] }, + { .field = "InErrors", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INERRORS] }, + { .field = "OutDatagrams", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_OUTDATAGRAMS] }, + { .field = "RcvbufErrors", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_RECVBUFERRORS] }, + { .field = "SndbufErrors", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_SNDBUFERRORS] }, + { .field = "InCsumErrors", + .offset = &_pm_proc_net_snmp.udp[_PM_SNMP_UDP_INCSUMERRORS] }, + { .field = NULL, .offset = NULL } +}; + +snmp_fields_t udplite_fields[] = { + { .field = "InDatagrams", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INDATAGRAMS] }, + { .field = "NoPorts", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_NOPORTS] }, + { .field = "InErrors", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INERRORS] }, + { .field = "OutDatagrams", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_OUTDATAGRAMS] }, + { .field = "RcvbufErrors", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_RECVBUFERRORS] }, + { .field = "SndbufErrors", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_SNDBUFERRORS] }, + { .field = "InCsumErrors", + .offset = &_pm_proc_net_snmp.udplite[_PM_SNMP_UDPLITE_INCSUMERRORS] }, + { .field = NULL, .offset = NULL } +}; + +static void +get_fields(snmp_fields_t *fields, char *header, char *buffer) +{ + int i, j, count; + char *p, *indices[SNMP_MAX_COLUMNS]; + + /* first get pointers to each of the column headings */ + strtok(header, " "); + for (i = 0; i < SNMP_MAX_COLUMNS; i++) { + if ((p = strtok(NULL, " \n")) == NULL) + break; + indices[i] = p; + } + count = i; + + /* + * Extract values via back-referencing column headings. + * "i" is the last found index, which we use for a bit + * of optimisation for the (common) in-order maps case + * (where "in order" means in the order defined by the + * passed in "fields" table which typically matches the + * kernel - but may be out-of-order for older kernels). + */ + strtok(buffer, " "); + for (i = j = 0; j < count && fields[i].field; j++, i++) { + if ((p = strtok(NULL, " \n")) == NULL) + break; + if (strcmp(fields[i].field, indices[j]) == 0) { + *fields[i].offset = strtoull(p, NULL, 10); + } else { + for (i = 0; fields[i].field; i++) { + if (strcmp(fields[i].field, indices[j]) != 0) + continue; + *fields[i].offset = strtoull(p, NULL, 10); + break; + } + if (fields[i].field == NULL) /* not found, ignore */ + i = 0; + } + } +} + +static void +get_ordinal_fields(snmp_fields_t *fields, char *header, char *buffer, + unsigned limit) +{ + int i, j, count; + unsigned int inst; + char *p, *indices[SNMP_MAX_COLUMNS]; + + strtok(header, " "); + for (i = 0; i < SNMP_MAX_COLUMNS; i++) { + if ((p = strtok(NULL, " \n")) == NULL) + break; + indices[i] = p; + } + count = i; + + strtok(buffer, " "); + for (j = 0; j < count; j++) { + if ((p = strtok(NULL, " \n")) == NULL) + break; + for (i = 0; fields[i].field; i++) { + if (sscanf(indices[j], fields[i].field, &inst) != 1) + continue; + if (inst >= limit) + continue; + *(fields[i].offset + inst) = strtoull(p, NULL, 10); + break; + } + } +} + +#define SNMP_IP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)ip_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.ip) +#define SNMP_ICMP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)icmp_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.icmp) +#define SNMP_ICMPMSG_OFFSET(ii, nn, pp) (int64_t *)((char *)pp + \ + (__psint_t)(icmpmsg_fields[ii].offset + nn) - (__psint_t)&_pm_proc_net_snmp.icmpmsg) +#define SNMP_TCP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)tcp_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.tcp) +#define SNMP_UDP_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)udp_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.udp) +#define SNMP_UDPLITE_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)udplite_fields[ii].offset - (__psint_t)&_pm_proc_net_snmp.udplite) + +static void +init_refresh_proc_net_snmp(proc_net_snmp_t *snmp) +{ + pmdaIndom *idp; + char *s; + int i, n; + + /* initially, all marked as "no value available" */ + for (i = 0; ip_fields[i].field != NULL; i++) + *(SNMP_IP_OFFSET(i, snmp->ip)) = -1; + for (i = 0; icmp_fields[i].field != NULL; i++) + *(SNMP_ICMP_OFFSET(i, snmp->icmp)) = -1; + for (i = 0; tcp_fields[i].field != NULL; i++) + *(SNMP_TCP_OFFSET(i, snmp->tcp)) = -1; + for (i = 0; udp_fields[i].field != NULL; i++) + *(SNMP_UDP_OFFSET(i, snmp->udp)) = -1; + for (i = 0; udplite_fields[i].field != NULL; i++) + *(SNMP_UDPLITE_OFFSET(i, snmp->udplite)) = -1; + for (i = 0; icmpmsg_fields[i].field != NULL; i++) + for (n = 0; n < NR_ICMPMSG_COUNTERS; n++) + *(SNMP_ICMPMSG_OFFSET(i, n, snmp->icmpmsg)) = -1; + + /* only need to allocate and setup the names once */ + if (proc_net_snmp_icmpmsg_names) + return; + i = NR_ICMPMSG_COUNTERS * SNMP_MAX_ICMPMSG_TYPESTR; + proc_net_snmp_icmpmsg_names = malloc(i); + if (!proc_net_snmp_icmpmsg_names) + return; + s = proc_net_snmp_icmpmsg_names; + for (n = 0; n < NR_ICMPMSG_COUNTERS; n++) { + sprintf(s, "Type%u", n); + _pm_proc_net_snmp_indom_id[n].i_name = s; + _pm_proc_net_snmp_indom_id[n].i_inst = n; + s += SNMP_MAX_ICMPMSG_TYPESTR; + } + idp = PMDAINDOM(ICMPMSG_INDOM); + idp->it_numinst = NR_ICMPMSG_COUNTERS; + idp->it_set = _pm_proc_net_snmp_indom_id; +} + +int +refresh_proc_net_snmp(proc_net_snmp_t *snmp) +{ + char buf[MAXPATHLEN]; + char header[1024]; + FILE *fp; + + init_refresh_proc_net_snmp(snmp); + if ((fp = linux_statsfile("/proc/net/snmp", buf, sizeof(buf))) == NULL) + return -oserror(); + while (fgets(header, sizeof(header), fp) != NULL) { + if (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "Ip:", 3) == 0) + get_fields(ip_fields, header, buf); + else if (strncmp(buf, "Icmp:", 5) == 0) + get_fields(icmp_fields, header, buf); + else if (strncmp(buf, "IcmpMsg:", 8) == 0) + get_ordinal_fields(icmpmsg_fields, header, buf, + NR_ICMPMSG_COUNTERS); + else if (strncmp(buf, "Tcp:", 4) == 0) + get_fields(tcp_fields, header, buf); + else if (strncmp(buf, "Udp:", 4) == 0) + get_fields(udp_fields, header, buf); + else if (strncmp(buf, "UdpLite:", 8) == 0) + get_fields(udplite_fields, header, buf); + else + fprintf(stderr, "Error: unrecognised snmp row: %s", buf); + } + } + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/proc_net_snmp.h b/src/pmdas/linux/proc_net_snmp.h new file mode 100644 index 0000000..eeeb2a6 --- /dev/null +++ b/src/pmdas/linux/proc_net_snmp.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define SNMP_MAX_COLUMNS 64 /* arbitrary upper bound */ +#define SNMP_PERLINE 16 /* see net/ipv4/proc.c */ +#define SNMP_MAX_ICMPMSG_TYPESTR 8 /* longest name for type */ +#define NR_ICMPMSG_COUNTERS 256 /* half of __ICMPMSG_MIB_MAX from kernel */ + +enum { + _PM_SNMP_IP_FORWARDING = 0, + _PM_SNMP_IP_DEFAULTTTL, + _PM_SNMP_IP_INRECEIVES, + _PM_SNMP_IP_INHDRERRORS, + _PM_SNMP_IP_INADDRERRORS, + _PM_SNMP_IP_FORWDATAGRAMS, + _PM_SNMP_IP_INUNKNOWNPROTOS, + _PM_SNMP_IP_INDISCARDS, + _PM_SNMP_IP_INDELIVERS, + _PM_SNMP_IP_OUTREQUESTS, + _PM_SNMP_IP_OUTDISCARDS, + _PM_SNMP_IP_OUTNOROUTES, + _PM_SNMP_IP_REASMTIMEOUT, + _PM_SNMP_IP_REASMREQDS, + _PM_SNMP_IP_REASMOKS, + _PM_SNMP_IP_REASMFAILS, + _PM_SNMP_IP_FRAGOKS, + _PM_SNMP_IP_FRAGFAILS, + _PM_SNMP_IP_FRAGCREATES, + + _PM_SNMP_IP_NFIELDS /* must be last */ +}; + +enum { + _PM_SNMP_ICMP_INMSGS = 0, + _PM_SNMP_ICMP_INERRORS, + _PM_SNMP_ICMP_INCSUMERRORS, + _PM_SNMP_ICMP_INDESTUNREACHS, + _PM_SNMP_ICMP_INTIMEEXCDS, + _PM_SNMP_ICMP_INPARMPROBS, + _PM_SNMP_ICMP_INSRCQUENCHS, + _PM_SNMP_ICMP_INREDIRECTS, + _PM_SNMP_ICMP_INECHOS, + _PM_SNMP_ICMP_INECHOREPS, + _PM_SNMP_ICMP_INTIMESTAMPS, + _PM_SNMP_ICMP_INTIMESTAMPREPS, + _PM_SNMP_ICMP_INADDRMASKS, + _PM_SNMP_ICMP_INADDRMASKREPS, + _PM_SNMP_ICMP_OUTMSGS, + _PM_SNMP_ICMP_OUTERRORS, + _PM_SNMP_ICMP_OUTDESTUNREACHS, + _PM_SNMP_ICMP_OUTTIMEEXCDS, + _PM_SNMP_ICMP_OUTPARMPROBS, + _PM_SNMP_ICMP_OUTSRCQUENCHS, + _PM_SNMP_ICMP_OUTREDIRECTS, + _PM_SNMP_ICMP_OUTECHOS, + _PM_SNMP_ICMP_OUTECHOREPS, + _PM_SNMP_ICMP_OUTTIMESTAMPS, + _PM_SNMP_ICMP_OUTTIMESTAMPREPS, + _PM_SNMP_ICMP_OUTADDRMASKS, + _PM_SNMP_ICMP_OUTADDRMASKREPS, + + _PM_SNMP_ICMP_NFIELDS /* must be last */ +}; + +enum { + _PM_SNMP_ICMPMSG_INTYPE = 0, + _PM_SNMP_ICMPMSG_OUTTYPE = NR_ICMPMSG_COUNTERS, + _PM_SNMP_ICMPMSG_NFIELDS = (NR_ICMPMSG_COUNTERS*2) +}; + +enum { + _PM_SNMP_TCP_RTOALGORITHM = 0, + _PM_SNMP_TCP_RTOMIN, + _PM_SNMP_TCP_RTOMAX, + _PM_SNMP_TCP_MAXCONN, + _PM_SNMP_TCP_ACTIVEOPENS, + _PM_SNMP_TCP_PASSIVEOPENS, + _PM_SNMP_TCP_ATTEMPTFAILS, + _PM_SNMP_TCP_ESTABRESETS, + _PM_SNMP_TCP_CURRESTAB, + _PM_SNMP_TCP_INSEGS, + _PM_SNMP_TCP_OUTSEGS, + _PM_SNMP_TCP_RETRANSSEGS, + _PM_SNMP_TCP_INERRS, + _PM_SNMP_TCP_OUTRSTS, + _PM_SNMP_TCP_INCSUMERRORS, + + _PM_SNMP_TCP_NFIELDS /* must be last */ +}; + +enum { + _PM_SNMP_UDP_INDATAGRAMS = 0, + _PM_SNMP_UDP_NOPORTS, + _PM_SNMP_UDP_INERRORS, + _PM_SNMP_UDP_OUTDATAGRAMS, + _PM_SNMP_UDP_RECVBUFERRORS, + _PM_SNMP_UDP_SNDBUFERRORS, + _PM_SNMP_UDP_INCSUMERRORS, + + _PM_SNMP_UDP_NFIELDS /* must be last */ +}; + +enum { + _PM_SNMP_UDPLITE_INDATAGRAMS = 0, + _PM_SNMP_UDPLITE_NOPORTS, + _PM_SNMP_UDPLITE_INERRORS, + _PM_SNMP_UDPLITE_OUTDATAGRAMS, + _PM_SNMP_UDPLITE_RECVBUFERRORS, + _PM_SNMP_UDPLITE_SNDBUFERRORS, + _PM_SNMP_UDPLITE_INCSUMERRORS, + + _PM_SNMP_UDPLITE_NFIELDS /* must be last */ +}; + +typedef struct { + __uint64_t ip[_PM_SNMP_IP_NFIELDS]; + __uint64_t icmp[_PM_SNMP_ICMP_NFIELDS]; + __uint64_t icmpmsg[_PM_SNMP_ICMPMSG_NFIELDS]; + __uint64_t tcp[_PM_SNMP_TCP_NFIELDS]; + __uint64_t udp[_PM_SNMP_UDP_NFIELDS]; + __uint64_t udplite[_PM_SNMP_UDPLITE_NFIELDS]; +} proc_net_snmp_t; + +extern int refresh_proc_net_snmp(proc_net_snmp_t *); diff --git a/src/pmdas/linux/proc_net_snmp_migrate.conf b/src/pmdas/linux/proc_net_snmp_migrate.conf new file mode 100644 index 0000000..4e3c4f7 --- /dev/null +++ b/src/pmdas/linux/proc_net_snmp_migrate.conf @@ -0,0 +1,8 @@ +# Copyright 2013 Red Hat. +# +# pmlogrewrite configuration for migrating archives containing old +# 32 bit /proc/net/snmp values to the 64 bit variants, matching up +# with changes in the metadata supplied by the PMDA (and the kernel). +# + +metric 60.14.* { type -> U64 } diff --git a/src/pmdas/linux/proc_net_sockstat.c b/src/pmdas/linux/proc_net_sockstat.c new file mode 100644 index 0000000..94f5a79 --- /dev/null +++ b/src/pmdas/linux/proc_net_sockstat.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include "proc_net_sockstat.h" + +int +refresh_proc_net_sockstat(proc_net_sockstat_t *proc_net_sockstat) +{ + char buf[1024]; + char fmt[64]; + FILE *fp; + + if ((fp = linux_statsfile("/proc/net/sockstat", buf, sizeof(buf))) == NULL) + return -oserror(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "TCP:", 4) == 0) { + sscanf(buf, "%s %s %d %s %d", fmt, fmt, + &proc_net_sockstat->tcp[_PM_SOCKSTAT_INUSE], fmt, + &proc_net_sockstat->tcp[_PM_SOCKSTAT_HIGHEST]); + proc_net_sockstat->tcp[_PM_SOCKSTAT_UTIL] = + proc_net_sockstat->tcp[_PM_SOCKSTAT_HIGHEST] != 0 ? + 100 * proc_net_sockstat->tcp[_PM_SOCKSTAT_INUSE] / + proc_net_sockstat->tcp[_PM_SOCKSTAT_HIGHEST] : 0; + } + else + if (strncmp(buf, "UDP:", 4) == 0) { + sscanf(buf, "%s %s %d %s %d", fmt, fmt, + &proc_net_sockstat->udp[_PM_SOCKSTAT_INUSE], fmt, + &proc_net_sockstat->udp[_PM_SOCKSTAT_HIGHEST]); + proc_net_sockstat->udp[_PM_SOCKSTAT_UTIL] = + proc_net_sockstat->udp[_PM_SOCKSTAT_HIGHEST] != 0 ? + 100 * proc_net_sockstat->udp[_PM_SOCKSTAT_INUSE] / + proc_net_sockstat->udp[_PM_SOCKSTAT_HIGHEST] : 0; + } + else + if (strncmp(buf, "RAW:", 4) == 0) { + sscanf(buf, "%s %s %d %s %d", fmt, fmt, + &proc_net_sockstat->raw[_PM_SOCKSTAT_INUSE], fmt, + &proc_net_sockstat->raw[_PM_SOCKSTAT_HIGHEST]); + proc_net_sockstat->raw[_PM_SOCKSTAT_UTIL] = + proc_net_sockstat->raw[_PM_SOCKSTAT_HIGHEST] != 0 ? + 100 * proc_net_sockstat->raw[_PM_SOCKSTAT_INUSE] / + proc_net_sockstat->raw[_PM_SOCKSTAT_HIGHEST] : 0; + } + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/proc_net_sockstat.h b/src/pmdas/linux/proc_net_sockstat.h new file mode 100644 index 0000000..4ad402e --- /dev/null +++ b/src/pmdas/linux/proc_net_sockstat.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _PM_SOCKSTAT_INUSE 0 +#define _PM_SOCKSTAT_HIGHEST 1 +#define _PM_SOCKSTAT_UTIL 2 +typedef struct { + int tcp[3]; + int udp[3]; + int raw[3]; +} proc_net_sockstat_t; + +extern int refresh_proc_net_sockstat(proc_net_sockstat_t *); + diff --git a/src/pmdas/linux/proc_net_tcp.c b/src/pmdas/linux/proc_net_tcp.c new file mode 100644 index 0000000..52f59b0 --- /dev/null +++ b/src/pmdas/linux/proc_net_tcp.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 1999,2004 Silicon Graphics, Inc. All Rights Reserved. + * This code contributed by Michal Kara (lemming@arthur.plbohnice.cz) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include "proc_net_tcp.h" + +#define MYBUFSZ (1<<14) /*16k*/ + +int +refresh_proc_net_tcp(proc_net_tcp_t *proc_net_tcp) +{ + FILE *fp; + char buf[MYBUFSZ]; + char *p = buf; + char *q; + unsigned int n; + ssize_t got = 0; + ptrdiff_t remnant = 0; + + memset(proc_net_tcp, 0, sizeof(*proc_net_tcp)); + + if ((fp = linux_statsfile("/proc/net/tcp", buf, sizeof(buf))) == NULL) + return -oserror(); + + /* skip header */ + if (fgets(buf, sizeof(buf), fp) == NULL) { + /* oops, no header! */ + fclose(fp); + return -oserror(); + } + for (buf[0]='\0';;) { + q = strchrnul(p, '\n'); + if (*q == '\n') { + if (1 == sscanf(p, " %*s %*s %*s %x", &n) + && n < _PM_TCP_LAST) { + proc_net_tcp->stat[n]++; + } + p = q + 1; + continue; + } + remnant = (q - p); + if (remnant > 0 && p != buf) + memmove(buf, p, remnant); + + got = read(fileno(fp), buf + remnant, MYBUFSZ - remnant - 1); + if (got <= 0) + break; + + buf[remnant + got] = '\0'; + p = buf; + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/proc_net_tcp.h b/src/pmdas/linux/proc_net_tcp.h new file mode 100644 index 0000000..87e662f --- /dev/null +++ b/src/pmdas/linux/proc_net_tcp.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1999,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This code contributed by Michal Kara (lemming@arthur.plbohnice.cz) + */ + +enum { + _PM_TCP_ESTABLISHED = 1, + _PM_TCP_SYN_SENT, + _PM_TCP_SYN_RECV, + _PM_TCP_FIN_WAIT1, + _PM_TCP_FIN_WAIT2, + _PM_TCP_TIME_WAIT, + _PM_TCP_CLOSE, + _PM_TCP_CLOSE_WAIT, + _PM_TCP_LAST_ACK, + _PM_TCP_LISTEN, + _PM_TCP_CLOSING, + _PM_TCP_LAST +}; + + +typedef struct { + int stat[_PM_TCP_LAST]; +} proc_net_tcp_t; + +extern int refresh_proc_net_tcp(proc_net_tcp_t *); + diff --git a/src/pmdas/linux/proc_partitions.c b/src/pmdas/linux/proc_partitions.c new file mode 100644 index 0000000..4901892 --- /dev/null +++ b/src/pmdas/linux/proc_partitions.c @@ -0,0 +1,808 @@ +/* + * Linux Partitions (disk and disk partition IO stats) Cluster + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2008,2012 Aconex. All Rights Reserved. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "convert.h" +#include "clusters.h" +#include "indom.h" +#include "proc_partitions.h" + +extern int _pm_numdisks; + +/* + * _pm_ispartition : return true if arg is a partition name + * return false if arg is a disk name + * ide disks are named e.g. hda + * ide partitions are named e.g. hda1 + * + * scsi disks are named e.g. sda + * scsi partitions are named e.g. sda1 + * + * device-mapper devices are named dm-[0-9]* and are mapped to their persistent + * name using the symlinks in /dev/mapper. + * + * devfs scsi disks are named e.g. scsi/host0/bus0/target1/lun0/disc + * devfs scsi partitions are named e.g. scsi/host0/bus0/target1/lun0/part1 + * + * Mylex raid disks are named e.g. rd/c0d0 or dac960/c0d0 + * Mylex raid partitions are named e.g. rd/c0d0p1 or dac960/c0d0p1 + * + * What this now tries to do is be a bit smarter, and guess that names + * with slashes that end in the form .../c0t0d0[p0], and ones without + * are good old 19th century device names like xx0 or xx0a. + */ +static int +_pm_isloop(char *dname) +{ + return strncmp(dname, "loop", 4) == 0; +} + +static int +_pm_isramdisk(char *dname) +{ + return strncmp(dname, "ram", 3) == 0; +} + +static int +_pm_ismmcdisk(char *dname) +{ + if (strncmp(dname, "mmcblk", 6) != 0) + return 0; + /* + * Are we a disk or a partition of the disk? If there is a "p" + * assume it is a partition - e.g. mmcblk0p6. + */ + return (strchr(dname + 6, 'p') == NULL); +} + +/* + * return true if arg is a device-mapper device + */ +static int +_pm_isdm(char *dname) +{ + return strncmp(dname, "dm-", 3) == 0; +} + +/* + * slight improvement to heuristic suggested by + * Tim Bradshaw on 29 Dec 2003 + */ +int +_pm_ispartition(char *dname) +{ + int p, m = strlen(dname) - 1; + + /* + * looking at something like foo/x, and we hope x ends p, for + * a partition, or not for a disk. + */ + if (strchr(dname, '/')) { + for (p = m; p > 0 && isdigit((int)dname[p]); p--) + ; + if (p == m) + /* name had no trailing digits. Wildly guess a disk. */ + return 1; + else + /* + * ends with digits, if preceding character is a 'p' punt + * on a partition + */ + return (dname[p] == 'p'? 1 : 0); + } + else { + /* + * default test : partition names end in a digit do not + * look like loopback devices. Handle other special-cases + * here - mostly seems to be RAM-type disk drivers that're + * choosing to end device names with numbers. + */ + return isdigit((int)dname[m]) && + !_pm_isloop(dname) && + !_pm_isramdisk(dname) && + !_pm_ismmcdisk(dname) && + !_pm_isdm(dname); + } +} + +/* + * return true is arg is an xvm volume name + */ +static int +_pm_isxvmvol(char *dname) +{ + return strstr(dname, "xvm") != NULL; +} + +/* + * return true is arg is a disk name + */ +static int +_pm_isdisk(char *dname) +{ + return !_pm_isloop(dname) && !_pm_isramdisk(dname) && !_pm_ispartition(dname) && !_pm_isxvmvol(dname) && !_pm_isdm(dname); +} + +static void +refresh_udev(pmInDom disk_indom, pmInDom partitions_indom) +{ + char buf[MAXNAMELEN]; + char realname[MAXNAMELEN]; + char *shortname; + char *p; + char *udevname; + FILE *pfp; + partitions_entry_t *entry; + int indom; + int inst; + + if (access("/dev/xscsi", R_OK) != 0) + return; + if (!(pfp = popen("find /dev/xscsi -name disc -o -name part[0-9]*", "r"))) + return; + while (fgets(buf, sizeof(buf), pfp)) { + if ((p = strrchr(buf, '\n')) != NULL) + *p = '\0'; + if (realpath(buf, realname)) { + udevname = buf + 5; /* /dev/xscsi/.... */ + if ((shortname = strrchr(realname, '/')) != NULL) { + shortname++; + indom = _pm_ispartition(shortname) ? + partitions_indom : disk_indom; + if (pmdaCacheLookupName(indom, shortname, &inst, (void **)&entry) != PMDA_CACHE_ACTIVE) + continue; + entry->udevnamebuf = strdup(udevname); + pmdaCacheStore(indom, PMDA_CACHE_HIDE, shortname, entry); /* inactive */ + pmdaCacheStore(indom, PMDA_CACHE_ADD, udevname, entry); /* active */ + } + } + } + pclose(pfp); +} + +/* + * Replace dm-* in namebuf with it's persistent name. This is a symlink in + * /dev/mapper/something -> ../dm-X where dm-X is currently in namebuf. Some + * older platforms (e.g. RHEL5) don't have the symlinks, just block devices + * in /dev/mapper. On newer kernels, the persistent name mapping is also + * exported via sysfs, which we use in preference. If this fails we leave + * the argument namebuf unaltered and return 0. + */ +static int +map_persistent_dm_name(char *namebuf, int namelen, int devmajor, int devminor) +{ + int fd; + char *p; + DIR *dp; + int found = 0; + struct dirent *dentry; + struct stat sb; + char path[MAXPATHLEN]; + + snprintf(path, sizeof(path), "%s/sys/block/%s/dm/name", linux_statspath, namebuf); + if ((fd = open(path, O_RDONLY)) >= 0) { + memset(path, 0, sizeof(path)); + if (read(fd, path, sizeof(path)) > 0) { + if ((p = strchr(path, '\n')) != NULL) + *p = '\0'; + strncpy(namebuf, path, MIN(sizeof(path), namelen)); + found = 1; + } + close(fd); + } + + if (!found) { + /* + * The sysfs name isn't available, so we'll have to walk /dev/mapper + * and match up dev_t instead [happens on RHEL5 and maybe elsewhere]. + */ + snprintf(path, sizeof(path), "%s/dev/mapper", linux_statspath); + if ((dp = opendir(path)) != NULL) { + while ((dentry = readdir(dp)) != NULL) { + snprintf(path, sizeof(path), + "%s/dev/mapper/%s", linux_statspath, dentry->d_name); + if (stat(path, &sb) != 0 || !S_ISBLK(sb.st_mode)) + continue; /* only interested in block devices */ + if (devmajor == major(sb.st_rdev) && devminor == minor(sb.st_rdev)) { + strncpy(namebuf, dentry->d_name, namelen); + found = 1; + break; + } + } + closedir(dp); + } + } + + return found; +} + +int +refresh_proc_partitions(pmInDom disk_indom, pmInDom partitions_indom, pmInDom dm_indom) +{ + FILE *fp; + int devmin; + int devmaj; + int n; + int indom; + int have_proc_diskstats; + int inst; + unsigned long long blocks; + partitions_entry_t *p; + int indom_changes = 0; + char *dmname; + char buf[MAXPATHLEN]; + char namebuf[MAXPATHLEN]; + static int first = 1; + + if (first) { + /* initialize the instance domain caches */ + pmdaCacheOp(disk_indom, PMDA_CACHE_LOAD); + pmdaCacheOp(partitions_indom, PMDA_CACHE_LOAD); + pmdaCacheOp(dm_indom, PMDA_CACHE_LOAD); + + first = 0; + indom_changes = 1; + } + + pmdaCacheOp(disk_indom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(partitions_indom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(dm_indom, PMDA_CACHE_INACTIVE); + + if ((fp = linux_statsfile("/proc/diskstats", buf, sizeof(buf))) != NULL) + /* 2.6 style disk stats */ + have_proc_diskstats = 1; + else { + if ((fp = linux_statsfile("/proc/partitions", buf, sizeof(buf))) != NULL) + have_proc_diskstats = 0; + else + return -oserror(); + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + dmname = NULL; + if (buf[0] != ' ' || buf[0] == '\n') { + /* skip heading */ + continue; + } + + if (have_proc_diskstats) { + if ((n = sscanf(buf, "%d %d %s", &devmaj, &devmin, namebuf)) != 3) + continue; + } + else { + /* /proc/partitions */ + if ((n = sscanf(buf, "%d %d %llu %s", &devmaj, &devmin, &blocks, namebuf)) != 4) + continue; + } + + if (_pm_isdm(namebuf)) { + indom = dm_indom; + dmname = strdup(namebuf); + } + else if (_pm_ispartition(namebuf)) + indom = partitions_indom; + else if (_pm_isdisk(namebuf)) + indom = disk_indom; + else + continue; + + if (indom == dm_indom) { + /* replace dm-[0-9]* with the persistent name from /dev/mapper */ + if (!map_persistent_dm_name(namebuf, sizeof(namebuf), devmaj, devmin)) { + /* skip dm devices that have no persistent name mapping */ + free(dmname); + continue; + } + } + + p = NULL; + if (pmdaCacheLookupName(indom, namebuf, &inst, (void **)&p) < 0 || !p) { + /* not found: allocate and add a new entry */ + p = (partitions_entry_t *)malloc(sizeof(partitions_entry_t)); + memset(p, 0, sizeof(partitions_entry_t)); + indom_changes++; + } + + if (p->dmname) + free(p->dmname); + p->dmname = dmname; /* NULL if not a dm device */ + + if (!p->namebuf) + p->namebuf = strdup(namebuf); + else + if (strcmp(namebuf, p->namebuf) != 0) { + free(p->namebuf); + p->namebuf = strdup(namebuf); + } + + /* activate this entry */ + if (p->udevnamebuf) + /* long xscsi name */ + inst = pmdaCacheStore(indom, PMDA_CACHE_ADD, p->udevnamebuf, p); + else + /* short /proc/diskstats or /proc/partitions name */ + inst = pmdaCacheStore(indom, PMDA_CACHE_ADD, namebuf, p); + + if (have_proc_diskstats) { + /* 2.6 style /proc/diskstats */ + p->nr_blocks = 0; + namebuf[0] = '\0'; + /* Linux source: block/genhd.c::diskstats_show(1) */ + n = sscanf(buf, "%u %u %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u", + &p->major, &p->minor, namebuf, + &p->rd_ios, &p->rd_merges, &p->rd_sectors, &p->rd_ticks, + &p->wr_ios, &p->wr_merges, &p->wr_sectors, &p->wr_ticks, + &p->ios_in_flight, &p->io_ticks, &p->aveq); + if (n != 14) { + p->rd_merges = p->wr_merges = p->wr_ticks = + p->ios_in_flight = p->io_ticks = p->aveq = 0; + /* Linux source: block/genhd.c::diskstats_show(2) */ + n = sscanf(buf, "%u %u %s %u %u %u %u\n", + &p->major, &p->minor, namebuf, + (unsigned int *)&p->rd_ios, (unsigned int *)&p->rd_sectors, + (unsigned int *)&p->wr_ios, (unsigned int *)&p->wr_sectors); + } + } + else { + /* 2.4 style /proc/partitions */ + namebuf[0] = '\0'; + n = sscanf(buf, "%u %u %lu %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u", + &p->major, &p->minor, &p->nr_blocks, namebuf, + &p->rd_ios, &p->rd_merges, &p->rd_sectors, + &p->rd_ticks, &p->wr_ios, &p->wr_merges, + &p->wr_sectors, &p->wr_ticks, &p->ios_in_flight, + &p->io_ticks, &p->aveq); + } + + } + + /* + * If any new disks or partitions have appeared then we + * we need to remap the long device names (if /dev/xscsi + * exists) and then flush the pmda cache. + * + * We just let inactive instances rot in the inactive state + * (this doesn't happen very often, so is only a minor leak). + */ + if (indom_changes) { + refresh_udev(disk_indom, partitions_indom); + pmdaCacheOp(disk_indom, PMDA_CACHE_SAVE); + pmdaCacheOp(partitions_indom, PMDA_CACHE_SAVE); + pmdaCacheOp(dm_indom, PMDA_CACHE_SAVE); + } + + /* + * success + */ + if (fp) + fclose(fp); + return 0; +} + +/* + * This table must always match the definitions in root_linux + * and metrictab[] in pmda.c + */ +static pmID disk_metric_table[] = { + /* disk.dev.read */ PMDA_PMID(CLUSTER_STAT,4), + /* disk.dev.write */ PMDA_PMID(CLUSTER_STAT,5), + /* disk.dev.total */ PMDA_PMID(CLUSTER_STAT,28), + /* disk.dev.blkread */ PMDA_PMID(CLUSTER_STAT,6), + /* disk.dev.blkwrite */ PMDA_PMID(CLUSTER_STAT,7), + /* disk.dev.blktotal */ PMDA_PMID(CLUSTER_STAT,36), + /* disk.dev.read_bytes */ PMDA_PMID(CLUSTER_STAT,38), + /* disk.dev.write_bytes */ PMDA_PMID(CLUSTER_STAT,39), + /* disk.dev.total_bytes */ PMDA_PMID(CLUSTER_STAT,40), + /* disk.dev.read_merge */ PMDA_PMID(CLUSTER_STAT,49), + /* disk.dev.write_merge */ PMDA_PMID(CLUSTER_STAT,50), + /* disk.dev.avactive */ PMDA_PMID(CLUSTER_STAT,46), + /* disk.dev.aveq */ PMDA_PMID(CLUSTER_STAT,47), + /* disk.dev.scheduler */ PMDA_PMID(CLUSTER_STAT,59), + /* disk.dev.read_rawactive */ PMDA_PMID(CLUSTER_STAT,72), + /* disk.dev.write_rawactive */ PMDA_PMID(CLUSTER_STAT,73), + + /* disk.all.read */ PMDA_PMID(CLUSTER_STAT,24), + /* disk.all.write */ PMDA_PMID(CLUSTER_STAT,25), + /* disk.all.total */ PMDA_PMID(CLUSTER_STAT,29), + /* disk.all.blkread */ PMDA_PMID(CLUSTER_STAT,26), + /* disk.all.blkwrite */ PMDA_PMID(CLUSTER_STAT,27), + /* disk.all.blktotal */ PMDA_PMID(CLUSTER_STAT,37), + /* disk.all.read_bytes */ PMDA_PMID(CLUSTER_STAT,41), + /* disk.all.write_bytes */ PMDA_PMID(CLUSTER_STAT,42), + /* disk.all.total_bytes */ PMDA_PMID(CLUSTER_STAT,43), + /* disk.all.read_merge */ PMDA_PMID(CLUSTER_STAT,51), + /* disk.all.write_merge */ PMDA_PMID(CLUSTER_STAT,52), + /* disk.all.avactive */ PMDA_PMID(CLUSTER_STAT,44), + /* disk.all.aveq */ PMDA_PMID(CLUSTER_STAT,45), + /* disk.all.read_rawactive */ PMDA_PMID(CLUSTER_STAT,74), + /* disk.all.write_rawactive */ PMDA_PMID(CLUSTER_STAT,75), + + /* disk.partitions.read */ PMDA_PMID(CLUSTER_PARTITIONS,0), + /* disk.partitions.write */ PMDA_PMID(CLUSTER_PARTITIONS,1), + /* disk.partitions.total */ PMDA_PMID(CLUSTER_PARTITIONS,2), + /* disk.partitions.blkread */ PMDA_PMID(CLUSTER_PARTITIONS,3), + /* disk.partitions.blkwrite */ PMDA_PMID(CLUSTER_PARTITIONS,4), + /* disk.partitions.blktotal */ PMDA_PMID(CLUSTER_PARTITIONS,5), + /* disk.partitions.read_bytes */ PMDA_PMID(CLUSTER_PARTITIONS,6), + /* disk.partitions.write_bytes */PMDA_PMID(CLUSTER_PARTITIONS,7), + /* disk.partitions.total_bytes */PMDA_PMID(CLUSTER_PARTITIONS,8), + + /* hinv.ndisk */ PMDA_PMID(CLUSTER_STAT,33), + + /* disk.dm.read */ PMDA_PMID(CLUSTER_DM,0), + /* disk.dm.write */ PMDA_PMID(CLUSTER_DM,1), + /* disk.dm.total */ PMDA_PMID(CLUSTER_DM,2), + /* disk.dm.blkread */ PMDA_PMID(CLUSTER_DM,3), + /* disk.dm.blkwrite */ PMDA_PMID(CLUSTER_DM,4), + /* disk.dm.blktotal */ PMDA_PMID(CLUSTER_DM,5), + /* disk.dm.read_bytes */ PMDA_PMID(CLUSTER_DM,6), + /* disk.dm.write_bytes */ PMDA_PMID(CLUSTER_DM,7), + /* disk.dm.total_bytes */ PMDA_PMID(CLUSTER_DM,8), + /* disk.dm.read_merge */ PMDA_PMID(CLUSTER_DM,9), + /* disk.dm.write_merge */ PMDA_PMID(CLUSTER_DM,10), + /* disk.dm.avactive */ PMDA_PMID(CLUSTER_DM,11), + /* disk.dm.aveq */ PMDA_PMID(CLUSTER_DM,12), + /* hinv.map.dmname */ PMDA_PMID(CLUSTER_DM,13), + /* disk.dm.read_rawactive */ PMDA_PMID(CLUSTER_DM,14), + /* disk.dm.write_rawactive */ PMDA_PMID(CLUSTER_DM,15), +}; + +int +is_partitions_metric(pmID full_pmid) +{ + int i; + static pmID *p = NULL; + __pmID_int *idp = (__pmID_int *)&(full_pmid); + pmID pmid = PMDA_PMID(idp->cluster, idp->item); + int n = sizeof(disk_metric_table) / sizeof(disk_metric_table[0]); + + if (p && *p == PMDA_PMID(idp->cluster, idp->item)) + return 1; + for (p = disk_metric_table, i=0; i < n; i++, p++) { + if (*p == pmid) + return 1; + } + return 0; +} + +char * +_pm_ioscheduler(const char *device) +{ + FILE *fp; + char *p, *q; + static char buf[1024]; + char path[MAXNAMELEN]; + + /* + * Extract scheduler from /sys/block//queue/scheduler. + * File format: "noop anticipatory [deadline] cfq" + * In older kernels (incl. RHEL5 and SLES10) this doesn't exist, + * but we can still look in /sys/block//queue/iosched to + * intuit the ones we know about (cfq, deadline, as, noop) based + * on the different status files they create. + */ + sprintf(path, "%s/sys/block/%s/queue/scheduler", linux_statspath, device); + if ((fp = fopen(path, "r")) != NULL) { + p = fgets(buf, sizeof(buf), fp); + fclose(fp); + if (!p) + goto unknown; + for (p = q = buf; p && *p && *p != ']'; p++) { + if (*p == '[') + q = p+1; + } + if (q == buf) + goto unknown; + if (*p != ']') + goto unknown; + *p = '\0'; + return q; + } + else { +#define BLKQUEUE "%s/sys/block/%s/queue/" + /* sniff around, maybe we'll get lucky and find something */ + sprintf(path, BLKQUEUE "iosched/quantum", linux_statspath, device); + if (access(path, F_OK) == 0) + return "cfq"; + sprintf(path, BLKQUEUE "iosched/fifo_batch", linux_statspath, device); + if (access(path, F_OK) == 0) + return "deadline"; + sprintf(path, BLKQUEUE "iosched/antic_expire", linux_statspath, device); + if (access(path, F_OK) == 0) + return "anticipatory"; + /* punt. noop has no files to match on ... */ + sprintf(path, BLKQUEUE "iosched", linux_statspath, device); + if (access(path, F_OK) == 0) + return "noop"; + /* else fall though ... */ +#undef BLKQUEUE + } + +unknown: + return "unknown"; +} + +int +proc_partitions_fetch(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int i; + partitions_entry_t *p = NULL; + + if (inst != PM_IN_NULL) { + if (pmdaCacheLookup(mdesc->m_desc.indom, inst, NULL, (void **)&p) < 0) + return PM_ERR_INST; + } + + switch (idp->cluster) { + case CLUSTER_STAT: + /* + * disk.{dev,all} remain in CLUSTER_STAT for backward compatibility + */ + switch(idp->item) { + case 4: /* disk.dev.read */ + if (p == NULL) + return PM_ERR_INST; + _pm_assign_ulong(atom, p->rd_ios); + break; + case 5: /* disk.dev.write */ + if (p == NULL) + return PM_ERR_INST; + _pm_assign_ulong(atom, p->wr_ios); + break; + case 6: /* disk.dev.blkread */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = p->rd_sectors; + break; + case 7: /* disk.dev.blkwrite */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = p->wr_sectors; + break; + case 28: /* disk.dev.total */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = p->rd_ios + p->wr_ios; + break; + case 36: /* disk.dev.blktotal */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = p->rd_sectors + p->wr_sectors; + break; + case 38: /* disk.dev.read_bytes */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = p->rd_sectors / 2; + break; + case 39: /* disk.dev.write_bytes */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = p->wr_sectors / 2; + break; + case 40: /* disk.dev.total_bytes */ + if (p == NULL) + return PM_ERR_INST; + atom->ull = (p->rd_sectors + p->wr_sectors) / 2; + break; + case 46: /* disk.dev.avactive ... already msec from /proc/diskstats */ + if (p == NULL) + return PM_ERR_INST; + atom->ul = p->io_ticks; + break; + case 47: /* disk.dev.aveq ... already msec from /proc/diskstats */ + if (p == NULL) + return PM_ERR_INST; + atom->ul = p->aveq; + break; + case 49: /* disk.dev.read_merge */ + if (p == NULL) + return PM_ERR_INST; + _pm_assign_ulong(atom, p->rd_merges); + break; + case 50: /* disk.dev.write_merge */ + if (p == NULL) + return PM_ERR_INST; + _pm_assign_ulong(atom, p->wr_merges); + break; + case 59: /* disk.dev.scheduler */ + if (p == NULL) + return PM_ERR_INST; + atom->cp = _pm_ioscheduler(p->namebuf); + break; + case 72: /* disk.dev.read_rawactive already ms from /proc/diskstats */ + if (p == NULL) + return PM_ERR_INST; + atom->ul = p->rd_ticks; + break; + case 73: /* disk.dev.write_rawactive already ms from /proc/diskstats */ + if (p == NULL) + return PM_ERR_INST; + atom->ul = p->wr_ticks; + break; + default: + /* disk.all.* is a singular instance domain */ + atom->ull = 0; + for (pmdaCacheOp(INDOM(DISK_INDOM), PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(INDOM(DISK_INDOM), PMDA_CACHE_WALK_NEXT)) < 0) + break; + if (!pmdaCacheLookup(INDOM(DISK_INDOM), i, NULL, (void **)&p) || !p) + continue; + switch (idp->item) { + case 24: /* disk.all.read */ + atom->ull += p->rd_ios; + break; + case 25: /* disk.all.write */ + atom->ull += p->wr_ios; + break; + case 26: /* disk.all.blkread */ + atom->ull += p->rd_sectors; + break; + case 27: /* disk.all.blkwrite */ + atom->ull += p->wr_sectors; + break; + case 29: /* disk.all.total */ + atom->ull += p->rd_ios + p->wr_ios; + break; + case 37: /* disk.all.blktotal */ + atom->ull += p->rd_sectors + p->wr_sectors; + break; + case 41: /* disk.all.read_bytes */ + atom->ull += p->rd_sectors / 2; + break; + case 42: /* disk.all.write_bytes */ + atom->ull += p->wr_sectors / 2; + break; + case 43: /* disk.all.total_bytes */ + atom->ull += (p->rd_sectors + p->wr_sectors) / 2; + break; + case 44: /* disk.all.avactive ... already msec from /proc/diskstats */ + atom->ull += p->io_ticks; + break; + case 45: /* disk.all.aveq ... already msec from /proc/diskstats */ + atom->ull += p->aveq; + break; + case 51: /* disk.all.read_merge */ + atom->ull += p->rd_merges; + break; + case 52: /* disk.all.write_merge */ + atom->ull += p->wr_merges; + break; + case 74: /* disk.all.read_rawactive ... already msec from /proc/diskstats */ + atom->ull += p->rd_ticks; + break; + case 75: /* disk.all.write_rawactive ... already msec from /proc/diskstats */ + atom->ull += p->wr_ticks; + break; + default: + return PM_ERR_PMID; + } + } /* loop */ + } + break; + + case CLUSTER_PARTITIONS: + if (p == NULL) + return PM_ERR_INST; + switch(idp->item) { + /* disk.partitions */ + case 0: /* disk.partitions.read */ + atom->ul = p->rd_ios; + break; + case 1: /* disk.partitions.write */ + atom->ul = p->wr_ios; + break; + case 2: /* disk.partitions.total */ + atom->ul = p->wr_ios + + p->rd_ios; + break; + case 3: /* disk.partitions.blkread */ + atom->ul = p->rd_sectors; + break; + case 4: /* disk.partitions.blkwrite */ + atom->ul = p->wr_sectors; + break; + case 5: /* disk.partitions.blktotal */ + atom->ul = p->rd_sectors + + p->wr_sectors; + break; + case 6: /* disk.partitions.read_bytes */ + atom->ul = p->rd_sectors / 2; + break; + case 7: /* disk.partitions.write_bytes */ + atom->ul = p->wr_sectors / 2; + break; + case 8: /* disk.partitions.total_bytes */ + atom->ul = (p->rd_sectors + + p->wr_sectors) / 2; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_DM: + if (p == NULL) + return PM_ERR_INST; + switch(idp->item) { + case 0: /* disk.dm.read */ + atom->ull = p->rd_ios; + break; + case 1: /* disk.dm.write */ + atom->ull = p->wr_ios; + break; + case 2: /* disk.dm.total */ + atom->ull = p->rd_ios + p->wr_ios; + break; + case 3: /* disk.dm.blkread */ + atom->ull = p->rd_sectors; + break; + case 4: /* disk.dm.blkwrite */ + atom->ull = p->wr_sectors; + break; + case 5: /* disk.dm.blktotal */ + atom->ull = p->rd_sectors + p->wr_sectors; + break; + case 6: /* disk.dm.read_bytes */ + atom->ull = p->rd_sectors / 2; + break; + case 7: /* disk.dm.write_bytes */ + atom->ull = p->wr_sectors / 2; + break; + case 8: /* disk.dm.total_bytes */ + atom->ull = (p->rd_sectors + p->wr_sectors) / 2; + break; + case 9: /* disk.dm.read_merge */ + atom->ull = p->rd_merges; + break; + case 10: /* disk.dm.write_merge */ + atom->ull = p->wr_merges; + break; + case 11: /* disk.dm.avactive */ + atom->ull = p->io_ticks; + break; + case 12: /* disk.dm.aveq */ + atom->ull = p->aveq; + break; + case 13: /* hinv.map.dmname */ + atom->cp = p->dmname; + break; + case 14: /* disk.dm.read_rawactive */ + atom->ul = p->rd_ticks; + break; + case 15: /* disk.dm.write_rawactive */ + atom->ul = p->wr_ticks; + break; + default: + return PM_ERR_PMID; + } + break; + + default: /* switch cluster */ + return PM_ERR_PMID; + } + + return 1; +} diff --git a/src/pmdas/linux/proc_partitions.h b/src/pmdas/linux/proc_partitions.h new file mode 100644 index 0000000..9348cde --- /dev/null +++ b/src/pmdas/linux/proc_partitions.h @@ -0,0 +1,44 @@ +/* + * Linux /proc/partitions metrics cluster + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct { + int id; + unsigned int major; + unsigned int minor; + unsigned long nr_blocks; + char *namebuf; /* from /proc/{partitions,diskstats} */ + char *udevnamebuf; /* from udev if we have it, else NULL */ + char *dmname; /* symlink from /dev/mapper, else NULL */ + unsigned long rd_ios; + unsigned long rd_merges; + unsigned long long rd_sectors; + unsigned int rd_ticks; + unsigned long wr_ios; + unsigned long wr_merges; + unsigned long long wr_sectors; + unsigned int wr_ticks; + unsigned int ios_in_flight; + unsigned int io_ticks; + unsigned int aveq; +} partitions_entry_t; + +extern int refresh_proc_partitions(pmInDom disk_indom, pmInDom partitions_indom, pmInDom dm_indom); +extern int is_partitions_metric(pmID); +extern int proc_partitions_fetch(pmdaMetric *, unsigned int, pmAtomValue *); diff --git a/src/pmdas/linux/proc_scsi.c b/src/pmdas/linux/proc_scsi.c new file mode 100644 index 0000000..2b3c952 --- /dev/null +++ b/src/pmdas/linux/proc_scsi.c @@ -0,0 +1,159 @@ +/* + * Linux Scsi Devices Cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "proc_scsi.h" + +static char diskname[64]; +static char tapename[64]; +static char cdromname[64]; + +int +refresh_proc_scsi(proc_scsi_t *scsi) +{ + char buf[1024]; + char name[1024]; + int i; + int n; + FILE *fp; + char *sp; + static int have_devfs = -1; + static int next_id = -1; + + if (next_id < 0) { + /* one trip initialization */ + next_id = 0; + + scsi->nscsi = 0; + scsi->scsi = (scsi_entry_t *)malloc(sizeof(scsi_entry_t)); + + /* scsi indom */ + scsi->scsi_indom->it_numinst = 0; + scsi->scsi_indom->it_set = (pmdaInstid *)malloc(sizeof(pmdaInstid)); + + /* devfs naming convention */ + have_devfs = access("/dev/.devfsd", F_OK) == 0; + if (have_devfs) { + strcpy(diskname, "scsi/host%d/bus%d/target%d/lun%d/disc"); + strcpy(tapename, "st0"); + strcpy(cdromname, "scd0"); + } + else { + strcpy(diskname, "sda"); + strcpy(tapename, "st0"); + strcpy(cdromname, "scd0"); + } + } + + if ((fp = linux_statsfile("/proc/scsi/scsi", buf, sizeof(buf))) == NULL) + return -oserror(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + scsi_entry_t x = { 0 }; + + if (strncmp(buf, "Host:", 5) != 0) + continue; + + n = sscanf(buf, "Host: scsi%d Channel: %d Id: %d Lun: %d", + &x.dev_host, &x.dev_channel, &x.dev_id, &x.dev_lun); + if (n != 4) + continue; + for (i=0; i < scsi->nscsi; i++) { + if (scsi->scsi[i].dev_host == x.dev_host && + scsi->scsi[i].dev_channel == x.dev_channel && + scsi->scsi[i].dev_id == x.dev_id && + scsi->scsi[i].dev_lun == x.dev_lun) + break; + } + + if (i == scsi->nscsi) { + scsi->nscsi++; + scsi->scsi = (scsi_entry_t *)realloc(scsi->scsi, + scsi->nscsi * sizeof(scsi_entry_t)); + memcpy(&scsi->scsi[i], &x, sizeof(scsi_entry_t)); + scsi->scsi[i].id = next_id++; + /* scan for the Vendor: and Type: strings */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((sp = strstr(buf, "Type:")) != (char *)NULL) { + if (sscanf(sp, "Type: %s", name) == 1) + scsi->scsi[i].dev_type = strdup(name); + else + scsi->scsi[i].dev_type = strdup("unknown"); + break; + } + } + + if (strcmp(scsi->scsi[i].dev_type, "Direct-Access") == 0) { + if (have_devfs) { + scsi->scsi[i].dev_name = (char *)malloc(64); + sprintf(scsi->scsi[i].dev_name, diskname, + scsi->scsi[i].dev_host, scsi->scsi[i].dev_channel, + scsi->scsi[i].dev_id, scsi->scsi[i].dev_lun); + } + else { + scsi->scsi[i].dev_name = strdup(diskname); + diskname[2]++; /* sd[a-z] bump to next disk device name */ + } + } + else + if (strcmp(scsi->scsi[i].dev_type, "Sequential-Access") == 0) { + scsi->scsi[i].dev_name = strdup(tapename); + tapename[2]++; /* st[0-9] bump to next tape device name */ + } + else + if (strcmp(scsi->scsi[i].dev_type, "CD-ROM") == 0) { + scsi->scsi[i].dev_name = strdup(cdromname); + cdromname[3]++; /* scd[0-9] bump to next CDROM device name */ + } + else + if (strcmp(scsi->scsi[i].dev_type, "Processor") == 0) + scsi->scsi[i].dev_name = strdup("SCSI Controller"); + else + scsi->scsi[i].dev_name = strdup("Unknown SCSI device"); + + sprintf(name, "scsi%d:%d:%d:%d %s", scsi->scsi[i].dev_host, + scsi->scsi[i].dev_channel, scsi->scsi[i].dev_id, scsi->scsi[i].dev_lun, scsi->scsi[i].dev_type); + scsi->scsi[i].namebuf = strdup(name); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_proc_scsi: add host=scsi%d channel=%d id=%d lun=%d type=%s\n", + scsi->scsi[i].dev_host, scsi->scsi[i].dev_channel, + scsi->scsi[i].dev_id, scsi->scsi[i].dev_lun, + scsi->scsi[i].dev_type); + } +#endif + } + } + + /* refresh scsi indom */ + if (scsi->scsi_indom->it_numinst != scsi->nscsi) { + scsi->scsi_indom->it_numinst = scsi->nscsi; + scsi->scsi_indom->it_set = (pmdaInstid *)realloc(scsi->scsi_indom->it_set, + scsi->nscsi * sizeof(pmdaInstid)); + memset(scsi->scsi_indom->it_set, 0, scsi->nscsi * sizeof(pmdaInstid)); + } + for (i=0; i < scsi->nscsi; i++) { + scsi->scsi_indom->it_set[i].i_inst = scsi->scsi[i].id; + scsi->scsi_indom->it_set[i].i_name = scsi->scsi[i].namebuf; + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/proc_scsi.h b/src/pmdas/linux/proc_scsi.h new file mode 100644 index 0000000..2ede392 --- /dev/null +++ b/src/pmdas/linux/proc_scsi.h @@ -0,0 +1,38 @@ +/* + * Linux /proc/scsi/scsi metrics cluster + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct { + int id; /* internal instance id */ + char *namebuf; /* external name, i.e. host:channel:id:lun */ + int dev_host; + int dev_channel; + int dev_id; + int dev_lun; + char *dev_type; + char *dev_name; +} scsi_entry_t; + +typedef struct { + int nscsi; + scsi_entry_t *scsi; + pmdaIndom *scsi_indom; +} proc_scsi_t; + +extern int refresh_proc_scsi(proc_scsi_t *); diff --git a/src/pmdas/linux/proc_slabinfo.c b/src/pmdas/linux/proc_slabinfo.c new file mode 100644 index 0000000..28dde3f --- /dev/null +++ b/src/pmdas/linux/proc_slabinfo.c @@ -0,0 +1,237 @@ +/* + * Linux Memory Slab Cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "proc_slabinfo.h" + +int +refresh_proc_slabinfo(proc_slabinfo_t *slabinfo) +{ + char buf[1024]; + slab_cache_t sbuf; + slab_cache_t *s; + FILE *fp; + int i, n; + int instcount; + char *w, *p; + int old_cache; + int err = 0; + static int next_id = -1; + static int major_version = -1; + static int minor_version = 0; + + if (next_id < 0) { + /* one trip initialization */ + next_id = 0; + + slabinfo->ncaches = 0; + slabinfo->caches = (slab_cache_t *)malloc(sizeof(slab_cache_t)); + slabinfo->indom->it_numinst = 0; + slabinfo->indom->it_set = (pmdaInstid *)malloc(sizeof(pmdaInstid)); + } + + if ((fp = linux_statsfile("/proc/slabinfo", buf, sizeof(buf))) == NULL) + return -oserror(); + + for (i=0; i < slabinfo->ncaches; i++) + slabinfo->caches[i].seen = 0; + + /* skip header */ + if (fgets(buf, sizeof(buf), fp) == NULL) { + /* oops, no header! */ + err = -oserror(); + goto out; + } + + if (major_version < 0) { + major_version = minor_version = 0; + if (strstr(buf, "slabinfo - version:")) { + char *p; + for (p=buf; *p; p++) { + if (isdigit((int)*p)) { + sscanf(p, "%d.%d", &major_version, &minor_version); + break; + } + } + } + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* try to convert whitespace in cache names to underscores, */ + /* by looking for alphabetic chars which follow whitespace. */ + if (buf[0] == '#') + continue; + for (w = NULL, p = buf; *p != '\0'; p++) { + if (isspace((int)*p)) + w = p; + else if (isdigit((int)*p)) + break; + else if (isalpha((int)*p) && w) { + for (; w && w != p; w++) + *w = '_'; + w = NULL; + } + } + + memset(&sbuf, 0, sizeof(slab_cache_t)); + + if (major_version == 1 && minor_version == 0) { + /* + * + * (generally 2.2 kernels) + */ + n = sscanf(buf, "%s %lu %lu", sbuf.name, + (unsigned long *)&sbuf.num_active_objs, + (unsigned long *)&sbuf.total_objs); + if (n != 3) { + err = PM_ERR_APPVERSION; + goto out; + } + } + else if (major_version == 1 && minor_version == 1) { + /* + * + * (generally 2.4 kernels) + */ + n = sscanf(buf, "%s %lu %lu %u %u %u %u", sbuf.name, + (unsigned long *)&sbuf.num_active_objs, + (unsigned long *)&sbuf.total_objs, + &sbuf.object_size, + &sbuf.num_active_slabs, + &sbuf.total_slabs, + &sbuf.pages_per_slab); + if (n != 7) { + err = PM_ERR_APPVERSION; + goto out; + } + + sbuf.total_size = sbuf.pages_per_slab * sbuf.num_active_slabs * _pm_system_pagesize; + } + else if (major_version == 2 && minor_version >= 0 && minor_version <= 1) { + /* + * .. and more + * (generally for kernels up to at least 2.6.11) + */ + n = sscanf(buf, "%s %lu %lu %u %u %u", sbuf.name, + (unsigned long *)&sbuf.num_active_objs, + (unsigned long *)&sbuf.total_objs, + &sbuf.object_size, + &sbuf.objects_per_slab, + &sbuf.pages_per_slab); + if (n != 6) { + err = PM_ERR_APPVERSION; + goto out; + } + + sbuf.total_size = sbuf.pages_per_slab * sbuf.num_active_objs * _pm_system_pagesize / sbuf.objects_per_slab; + } + else { + /* no support */ + err = PM_ERR_APPVERSION; + goto out; + } + + old_cache = -1; + for (i=0; i < slabinfo->ncaches; i++) { + if (strcmp(slabinfo->caches[i].name, sbuf.name) == 0) { + if (slabinfo->caches[i].valid) + break; + else + old_cache = i; + } + } + + if (i == slabinfo->ncaches) { + /* new cache has appeared */ + if (old_cache >= 0) { + /* same cache as last time : reuse the id */ + i = old_cache; + } + else { + slabinfo->ncaches++; + slabinfo->caches = (slab_cache_t *)realloc(slabinfo->caches, + slabinfo->ncaches * sizeof(slab_cache_t)); + slabinfo->caches[i].id = next_id++; + } + slabinfo->caches[i].valid = 1; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_slabinfo: add \"%s\"\n", sbuf.name); + } +#endif + } + + s = &slabinfo->caches[i]; + strcpy(s->name, sbuf.name); + s->num_active_objs = sbuf.num_active_objs; + s->total_objs = sbuf.total_objs; + s->object_size = sbuf.object_size; + s->num_active_slabs = sbuf.num_active_slabs; + s->total_slabs = sbuf.total_slabs; + s->pages_per_slab = sbuf.pages_per_slab; + s->objects_per_slab = sbuf.objects_per_slab; + s->total_size = sbuf.total_size; + + s->seen = major_version * 10 + minor_version; + } + + /* check for caches that have been deleted (eg. by rmmod) */ + for (i=0, instcount=0; i < slabinfo->ncaches; i++) { + if (slabinfo->caches[i].valid) { + if (slabinfo->caches[i].seen == 0) { + slabinfo->caches[i].valid = 0; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_slabinfo: drop \"%s\"\n", slabinfo->caches[i].name); + } +#endif + } + else { + instcount++; + } + } + } + + /* refresh slabinfo indom */ + if (slabinfo->indom->it_numinst != instcount) { + slabinfo->indom->it_numinst = instcount; + slabinfo->indom->it_set = (pmdaInstid *)realloc(slabinfo->indom->it_set, + instcount * sizeof(pmdaInstid)); + memset(slabinfo->indom->it_set, 0, instcount * sizeof(pmdaInstid)); + } + for (n=0, i=0; i < slabinfo->ncaches; i++) { + if (slabinfo->caches[i].valid) { + slabinfo->indom->it_set[n].i_inst = slabinfo->caches[i].id; + slabinfo->indom->it_set[n].i_name = slabinfo->caches[i].name; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_slabinfo: cache[%d] = \"%s\"\n", + n, slabinfo->indom->it_set[n].i_name); + } +#endif + n++; + } + } + +out: + fclose(fp); + return err; +} diff --git a/src/pmdas/linux/proc_slabinfo.h b/src/pmdas/linux/proc_slabinfo.h new file mode 100644 index 0000000..92d4e9c --- /dev/null +++ b/src/pmdas/linux/proc_slabinfo.h @@ -0,0 +1,53 @@ +/* + * Linux /proc/slabinfo metrics cluster + * + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/*** version 1.1 + "cache-name" num-active-objs total-objs object-size num-active-slabs \ + total-slabs num-pages-per-slab + + further values (not exported) on SMP and with statistics enabled + *** version 1.0 + "cache-name" num-active-objs total-objs + ***/ + +typedef struct { + int id; + int seen; /* have seen this time, and num values seen */ + int valid; + char name[64]; + __uint64_t num_active_objs; + __uint64_t total_objs; + __uint32_t object_size; + __uint64_t total_size; + __uint32_t num_active_slabs; + __uint32_t objects_per_slab; + __uint32_t total_slabs; + __uint32_t pages_per_slab; +} slab_cache_t; + +typedef struct { + int ncaches; + slab_cache_t *caches; + pmdaIndom *indom; +} proc_slabinfo_t; + +extern size_t _pm_system_pagesize; + +extern int refresh_proc_slabinfo(proc_slabinfo_t *); + diff --git a/src/pmdas/linux/proc_stat.c b/src/pmdas/linux/proc_stat.c new file mode 100644 index 0000000..6a8a798 --- /dev/null +++ b/src/pmdas/linux/proc_stat.c @@ -0,0 +1,304 @@ +/* + * Linux /proc/stat metrics cluster + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2008-2009 Aconex. All Rights Reserved. + * Copyright (c) 2000,2004-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include +#include +#include +#include "proc_cpuinfo.h" +#include "proc_stat.h" + +int +refresh_proc_stat(proc_cpuinfo_t *proc_cpuinfo, proc_stat_t *proc_stat) +{ + pmdaIndom *idp = PMDAINDOM(CPU_INDOM); + char buf[MAXPATHLEN]; + char fmt[64]; + static int fd = -1; /* kept open until exit() */ + static int started; + static char *statbuf; + static int maxstatbuf; + static char **bufindex; + static int nbufindex; + static int maxbufindex; + int size; + int n; + int i; + int j; + + if (fd >= 0) { + if (lseek(fd, 0, SEEK_SET) < 0) + return -oserror(); + } else { + snprintf(buf, sizeof(buf), "%s/proc/stat", linux_statspath); + if ((fd = open(buf, O_RDONLY)) < 0) + return -oserror(); + } + + for (n=0;;) { + while (n >= maxstatbuf) { + size = maxstatbuf + 512; + if ((statbuf = (char *)realloc(statbuf, size)) == NULL) + return -ENOMEM; + maxstatbuf = size; + } + size = (statbuf + maxstatbuf) - (statbuf + n); + if ((i = read(fd, statbuf + n, size)) > 0) + n += i; + else + break; + } + statbuf[n] = '\0'; + + if (bufindex == NULL) { + size = 4 * sizeof(char *); + if ((bufindex = (char **)malloc(size)) == NULL) + return -ENOMEM; + maxbufindex = 4; + } + + nbufindex = 0; + bufindex[nbufindex] = statbuf; + for (i=0; i < n; i++) { + if (statbuf[i] == '\n' || statbuf[i] == '\0') { + statbuf[i] = '\0'; + if (nbufindex + 1 >= maxbufindex) { + size = (maxbufindex + 4) * sizeof(char *); + if ((bufindex = (char **)realloc(bufindex, size)) == NULL) + return -ENOMEM; + maxbufindex += 4; + } + bufindex[++nbufindex] = statbuf + i + 1; + } + } + + if (!started) { + started = 1; + memset(proc_stat, 0, sizeof(*proc_stat)); + + /* hz of running kernel */ + proc_stat->hz = sysconf(_SC_CLK_TCK); + + /* scan ncpus */ + for (i=0; i < nbufindex; i++) { + if (strncmp("cpu", bufindex[i], 3) == 0 && isdigit((int)bufindex[i][3])) + proc_stat->ncpu++; + } + if (proc_stat->ncpu == 0) + proc_stat->ncpu = 1; /* non-SMP kernel? */ + proc_stat->cpu_indom = idp; + proc_stat->cpu_indom->it_numinst = proc_stat->ncpu; + proc_stat->cpu_indom->it_set = (pmdaInstid *)malloc( + proc_stat->ncpu * sizeof(pmdaInstid)); + /* + * Map out the CPU instance domain. + * + * The first call to cpu_name() does initialization on the + * proc_cpuinfo structure. + */ + for (i=0; i < proc_stat->ncpu; i++) { + proc_stat->cpu_indom->it_set[i].i_inst = i; + proc_stat->cpu_indom->it_set[i].i_name = cpu_name(proc_cpuinfo, i); + } + + n = proc_stat->ncpu * sizeof(unsigned long long); + proc_stat->p_user = (unsigned long long *)calloc(1, n); + proc_stat->p_nice = (unsigned long long *)calloc(1, n); + proc_stat->p_sys = (unsigned long long *)calloc(1, n); + proc_stat->p_idle = (unsigned long long *)calloc(1, n); + proc_stat->p_wait = (unsigned long long *)calloc(1, n); + proc_stat->p_irq = (unsigned long long *)calloc(1, n); + proc_stat->p_sirq = (unsigned long long *)calloc(1, n); + proc_stat->p_steal = (unsigned long long *)calloc(1, n); + proc_stat->p_guest = (unsigned long long *)calloc(1, n); + + n = proc_cpuinfo->node_indom->it_numinst * sizeof(unsigned long long); + proc_stat->n_user = calloc(1, n); + proc_stat->n_nice = calloc(1, n); + proc_stat->n_sys = calloc(1, n); + proc_stat->n_idle = calloc(1, n); + proc_stat->n_wait = calloc(1, n); + proc_stat->n_irq = calloc(1, n); + proc_stat->n_sirq = calloc(1, n); + proc_stat->n_steal = calloc(1, n); + proc_stat->n_guest = calloc(1, n); + } + else { + /* reset per-node stats */ + n = proc_cpuinfo->node_indom->it_numinst * sizeof(unsigned long long); + memset(proc_stat->n_user, 0, n); + memset(proc_stat->n_nice, 0, n); + memset(proc_stat->n_sys, 0, n); + memset(proc_stat->n_idle, 0, n); + memset(proc_stat->n_wait, 0, n); + memset(proc_stat->n_irq, 0, n); + memset(proc_stat->n_sirq, 0, n); + memset(proc_stat->n_steal, 0, n); + memset(proc_stat->n_guest, 0, n); + } + /* + * cpu 95379 4 20053 6502503 + * 2.6 kernels have 3 additional fields + * for wait, irq and soft_irq. + */ + strcpy(fmt, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu"); + n = sscanf((const char *)bufindex[0], fmt, + &proc_stat->user, &proc_stat->nice, + &proc_stat->sys, &proc_stat->idle, + &proc_stat->wait, &proc_stat->irq, + &proc_stat->sirq, &proc_stat->steal, + &proc_stat->guest); + + /* + * per-cpu stats + * e.g. cpu0 95379 4 20053 6502503 + * 2.6 kernels have 3 additional fields for wait, irq and soft_irq. + * More recent (2008) 2.6 kernels have an extra field for guest. + */ + if (proc_stat->ncpu == 1) { + /* + * Don't bother scanning - the counters are the same + * as for "all" cpus, as already scanned above. + * This also handles the non-SMP code where + * there is no line starting with "cpu0". + */ + proc_stat->p_user[0] = proc_stat->user; + proc_stat->p_nice[0] = proc_stat->nice; + proc_stat->p_sys[0] = proc_stat->sys; + proc_stat->p_idle[0] = proc_stat->idle; + proc_stat->p_wait[0] = proc_stat->wait; + proc_stat->p_irq[0] = proc_stat->irq; + proc_stat->p_sirq[0] = proc_stat->sirq; + proc_stat->p_steal[0] = proc_stat->steal; + proc_stat->p_guest[0] = proc_stat->guest; + } + else { + strcpy(fmt, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu"); + for (i=0; i < proc_stat->ncpu; i++) { + for (j=0; j < nbufindex; j++) { + if (strncmp("cpu", bufindex[j], 3) == 0 && isdigit((int)bufindex[j][3])) { + int c; + int cpunum = atoi(&bufindex[j][3]); + int node; + if (cpunum >= 0 && cpunum < proc_stat->ncpu) { + n = sscanf(bufindex[j], fmt, &c, + &proc_stat->p_user[cpunum], + &proc_stat->p_nice[cpunum], + &proc_stat->p_sys[cpunum], + &proc_stat->p_idle[cpunum], + &proc_stat->p_wait[cpunum], + &proc_stat->p_irq[cpunum], + &proc_stat->p_sirq[cpunum], + &proc_stat->p_steal[cpunum], + &proc_stat->p_guest[cpunum]); + if ((node = proc_cpuinfo->cpuinfo[cpunum].node) != -1) { + proc_stat->n_user[node] += proc_stat->p_user[cpunum]; + proc_stat->n_nice[node] += proc_stat->p_nice[cpunum]; + proc_stat->n_sys[node] += proc_stat->p_sys[cpunum]; + proc_stat->n_idle[node] += proc_stat->p_idle[cpunum]; + proc_stat->n_wait[node] += proc_stat->p_wait[cpunum]; + proc_stat->n_irq[node] += proc_stat->p_irq[cpunum]; + proc_stat->n_sirq[node] += proc_stat->p_sirq[cpunum]; + proc_stat->n_steal[node] += proc_stat->p_steal[cpunum]; + proc_stat->n_guest[node] += proc_stat->p_guest[cpunum]; + } + } + } + } + if (j == nbufindex) + break; + } + } + + /* + * page 59739 34786 + * Note: this has moved to /proc/vmstat in 2.6 kernels + */ + strcpy(fmt, "page %u %u"); + for (j=0; j < nbufindex; j++) { + if (strncmp(fmt, bufindex[j], 5) == 0) { + sscanf((const char *)bufindex[j], fmt, + &proc_stat->page[0], &proc_stat->page[1]); + break; + } + } + + /* + * swap 0 1 + * Note: this has moved to /proc/vmstat in 2.6 kernels + */ + strcpy(fmt, "swap %u %u"); + for (j=0; j < nbufindex; j++) { + if (strncmp(fmt, bufindex[j], 5) == 0) { + sscanf((const char *)bufindex[j], fmt, + &proc_stat->swap[0], &proc_stat->swap[1]); + break; + } + } + + /* + * intr 32845463 24099228 2049 0 2 .... + * (just export the first number, which is total interrupts) + */ + strcpy(fmt, "intr %llu"); + for (j=0; j < nbufindex; j++) { + if (strncmp(fmt, bufindex[j], 5) == 0) { + sscanf((const char *)bufindex[j], fmt, &proc_stat->intr); + break; + } + } + + /* + * ctxt 1733480 + */ + strcpy(fmt, "ctxt %llu"); + for (j=0; j < nbufindex; j++) { + if (strncmp(fmt, bufindex[j], 5) == 0) { + sscanf((const char *)bufindex[j], fmt, &proc_stat->ctxt); + break; + } + } + + /* + * btime 1733480 + */ + strcpy(fmt, "btime %lu"); + for (j=0; j < nbufindex; j++) { + if (strncmp(fmt, bufindex[j], 6) == 0) { + sscanf((const char *)bufindex[j], fmt, &proc_stat->btime); + break; + } + } + + /* + * processes 2213 + */ + strcpy(fmt, "processes %lu"); + for (j=0; j < nbufindex; j++) { + if (strncmp(fmt, bufindex[j], 10) == 0) { + sscanf((const char *)bufindex[j], fmt, &proc_stat->processes); + break; + } + } + + /* success */ + return 0; +} diff --git a/src/pmdas/linux/proc_stat.h b/src/pmdas/linux/proc_stat.h new file mode 100644 index 0000000..78b2c09 --- /dev/null +++ b/src/pmdas/linux/proc_stat.h @@ -0,0 +1,65 @@ +/* + * Linux /proc/stat metrics cluster + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2008 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct { + unsigned long long user; + unsigned long long sys; + unsigned long long nice; + unsigned long long idle; + unsigned long long wait; + unsigned long long irq; + unsigned long long sirq; + unsigned long long steal; + unsigned long long guest; + unsigned int ncpu; + /* per-cpu */ + unsigned long long *p_user; + unsigned long long *p_sys; + unsigned long long *p_nice; + unsigned long long *p_idle; + unsigned long long *p_wait; + unsigned long long *p_irq; + unsigned long long *p_sirq; + unsigned long long *p_steal; + unsigned long long *p_guest; + /* per-node */ + unsigned long long *n_user; + unsigned long long *n_sys; + unsigned long long *n_nice; + unsigned long long *n_idle; + unsigned long long *n_wait; + unsigned long long *n_irq; + unsigned long long *n_sirq; + unsigned long long *n_steal; + unsigned long long *n_guest; + + unsigned int ndisk; + unsigned int page[2]; /* unused in 2.6 now in /proc/vmstat */ + unsigned int swap[2]; /* unused in 2.6 now in /proc/vmstat */ + unsigned long long intr; + unsigned long long ctxt; + unsigned long btime; + unsigned long processes; + pmdaIndom *cpu_indom; + unsigned int hz; +} proc_stat_t; + +extern int refresh_proc_stat(proc_cpuinfo_t *, proc_stat_t *); diff --git a/src/pmdas/linux/proc_sys_fs.c b/src/pmdas/linux/proc_sys_fs.c new file mode 100644 index 0000000..021ae23 --- /dev/null +++ b/src/pmdas/linux/proc_sys_fs.c @@ -0,0 +1,80 @@ +/* + * Linux /proc/sys/fs metrics cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2003,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "proc_sys_fs.h" + +int +refresh_proc_sys_fs(proc_sys_fs_t *proc_sys_fs) +{ + static int err_reported; + char buf[MAXPATHLEN]; + FILE *filesp = NULL; + FILE *inodep = NULL; + FILE *dentryp = NULL; + + memset(proc_sys_fs, 0, sizeof(proc_sys_fs_t)); + + if ((filesp = linux_statsfile("/proc/sys/fs/file-nr", buf, sizeof(buf))) == NULL || + (inodep = linux_statsfile("/proc/sys/fs/inode-state", buf, sizeof(buf))) == NULL || + (dentryp = linux_statsfile("/proc/sys/fs/dentry-state", buf, sizeof(buf))) == NULL) { + proc_sys_fs->errcode = -oserror(); + if (err_reported == 0) + fprintf(stderr, "Warning: vfs metrics are not available : %s\n", + osstrerror()); + } + else { + proc_sys_fs->errcode = 0; + if (fscanf(filesp, "%d %d %d", + &proc_sys_fs->fs_files_count, + &proc_sys_fs->fs_files_free, + &proc_sys_fs->fs_files_max) != 3) + proc_sys_fs->errcode = PM_ERR_VALUE; + if (fscanf(inodep, "%d %d", + &proc_sys_fs->fs_inodes_count, + &proc_sys_fs->fs_inodes_free) != 2) + proc_sys_fs->errcode = PM_ERR_VALUE; + if (fscanf(dentryp, "%d %d", + &proc_sys_fs->fs_dentry_count, + &proc_sys_fs->fs_dentry_free) != 2) + proc_sys_fs->errcode = PM_ERR_VALUE; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + if (proc_sys_fs->errcode == 0) + fprintf(stderr, "refresh_proc_sys_fs: found vfs metrics\n"); + else + fprintf(stderr, "refresh_proc_sys_fs: botch! missing vfs metrics\n"); + } +#endif + } + if (filesp) + fclose(filesp); + if (inodep) + fclose(inodep); + if (dentryp) + fclose(dentryp); + + if (!err_reported) + err_reported = 1; + + if (proc_sys_fs->errcode == 0) + return 0; + return -1; +} diff --git a/src/pmdas/linux/proc_sys_fs.h b/src/pmdas/linux/proc_sys_fs.h new file mode 100644 index 0000000..d1a1ebf --- /dev/null +++ b/src/pmdas/linux/proc_sys_fs.h @@ -0,0 +1,32 @@ +/* + * Linux /proc/sys/fs metrics cluster + * + * Copyright (c) 2003,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct { + int errcode; /* error from previous refresh */ + int fs_files_count; + int fs_files_free; + int fs_files_max; + int fs_inodes_count; + int fs_inodes_free; + int fs_dentry_count; + int fs_dentry_free; +} proc_sys_fs_t; + +extern int refresh_proc_sys_fs(proc_sys_fs_t *); diff --git a/src/pmdas/linux/proc_uptime.c b/src/pmdas/linux/proc_uptime.c new file mode 100644 index 0000000..0d56bfd --- /dev/null +++ b/src/pmdas/linux/proc_uptime.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) Red Hat 2014. + * Copyright (c) International Business Machines Corp., 2002 + * This code contributed by Mike Mason + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include "proc_uptime.h" + +int +refresh_proc_uptime(proc_uptime_t *proc_uptime) +{ + char buf[MAXPATHLEN]; + int fd, n; + float uptime = 0.0, idletime = 0.0; + + memset(proc_uptime, 0, sizeof(proc_uptime_t)); + snprintf(buf, sizeof(buf), "%s/proc/uptime", linux_statspath); + if ((fd = open(buf, O_RDONLY)) < 0) + return -oserror(); + + n = read(fd, buf, sizeof(buf)); + close(fd); + if (n < 0) + return -oserror(); + else if (n > 0) + n--; + buf[n] = '\0'; + + sscanf((const char *)buf, "%f %f", &uptime, &idletime); + proc_uptime->uptime = (unsigned long) uptime; + proc_uptime->idletime = (unsigned long) idletime; + return 0; +} diff --git a/src/pmdas/linux/proc_uptime.h b/src/pmdas/linux/proc_uptime.h new file mode 100644 index 0000000..f4a658f --- /dev/null +++ b/src/pmdas/linux/proc_uptime.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This code contributed by Mike Mason + */ + +typedef struct { + unsigned long uptime; + unsigned long idletime; +} proc_uptime_t; + +extern int refresh_proc_uptime(proc_uptime_t *); + diff --git a/src/pmdas/linux/proc_vmstat.c b/src/pmdas/linux/proc_vmstat.c new file mode 100644 index 0000000..f56c3c3 --- /dev/null +++ b/src/pmdas/linux/proc_vmstat.c @@ -0,0 +1,299 @@ +/* + * Linux /proc/vmstat metrics cluster + * + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2007,2011 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "pmda.h" +#include "indom.h" +#include "proc_vmstat.h" + +static struct { + const char *field; + __uint64_t *offset; +} vmstat_fields[] = { + /* sorted by name to make maintenance easier */ + { .field = "allocstall", + .offset = &_pm_proc_vmstat.allocstall }, + { .field = "compact_blocks_moved", + .offset = &_pm_proc_vmstat.compact_blocks_moved }, + { .field = "compact_fail", + .offset = &_pm_proc_vmstat.compact_fail }, + { .field = "compact_pagemigrate_failed", + .offset = &_pm_proc_vmstat.compact_pagemigrate_failed }, + { .field = "compact_pages_moved", + .offset = &_pm_proc_vmstat.compact_pages_moved }, + { .field = "compact_stall", + .offset = &_pm_proc_vmstat.compact_stall }, + { .field = "compact_success", + .offset = &_pm_proc_vmstat.compact_success }, + { .field = "htlb_buddy_alloc_fail", + .offset = &_pm_proc_vmstat.htlb_buddy_alloc_fail }, + { .field = "htlb_buddy_alloc_success", + .offset = &_pm_proc_vmstat.htlb_buddy_alloc_success }, + { .field = "kswapd_inodesteal", + .offset = &_pm_proc_vmstat.kswapd_inodesteal }, + { .field = "kswapd_low_wmark_hit_quickly", + .offset = &_pm_proc_vmstat.kswapd_low_wmark_hit_quickly }, + { .field = "kswapd_high_wmark_hit_quickly", + .offset = &_pm_proc_vmstat.kswapd_high_wmark_hit_quickly }, + { .field = "kswapd_skip_congestion_wait", + .offset = &_pm_proc_vmstat.kswapd_skip_congestion_wait }, + { .field = "kswapd_steal", + .offset = &_pm_proc_vmstat.kswapd_steal }, + { .field = "nr_active_anon", + .offset = &_pm_proc_vmstat.nr_active_anon }, + { .field = "nr_active_file", + .offset = &_pm_proc_vmstat.nr_active_file }, + { .field = "nr_anon_pages", + .offset = &_pm_proc_vmstat.nr_anon_pages }, + { .field = "nr_anon_transparent_hugepages", + .offset = &_pm_proc_vmstat.nr_anon_transparent_hugepages }, + { .field = "nr_bounce", + .offset = &_pm_proc_vmstat.nr_bounce }, + { .field = "nr_dirty", + .offset = &_pm_proc_vmstat.nr_dirty }, + { .field = "nr_dirtied", + .offset = &_pm_proc_vmstat.nr_dirtied }, + { .field = "nr_dirty_threshold", + .offset = &_pm_proc_vmstat.nr_dirty_threshold }, + { .field = "nr_dirty_background_threshold", + .offset = &_pm_proc_vmstat.nr_dirty_background_threshold }, + { .field = "nr_file_pages", + .offset = &_pm_proc_vmstat.nr_file_pages }, + { .field = "nr_free_pages", + .offset = &_pm_proc_vmstat.nr_free_pages }, + { .field = "nr_inactive_anon", + .offset = &_pm_proc_vmstat.nr_inactive_anon }, + { .field = "nr_inactive_file", + .offset = &_pm_proc_vmstat.nr_inactive_file }, + { .field = "nr_isolated_anon", + .offset = &_pm_proc_vmstat.nr_isolated_anon }, + { .field = "nr_isolated_file", + .offset = &_pm_proc_vmstat.nr_isolated_file }, + { .field = "nr_kernel_stack", + .offset = &_pm_proc_vmstat.nr_kernel_stack }, + { .field = "nr_mapped", + .offset = &_pm_proc_vmstat.nr_mapped }, + { .field = "nr_mlock", + .offset = &_pm_proc_vmstat.nr_mlock }, + { .field = "nr_page_table_pages", + .offset = &_pm_proc_vmstat.nr_page_table_pages }, + { .field = "nr_shmem", + .offset = &_pm_proc_vmstat.nr_shmem }, + { .field = "nr_slab_reclaimable", + .offset = &_pm_proc_vmstat.nr_slab_reclaimable }, + { .field = "nr_slab_unreclaimable", + .offset = &_pm_proc_vmstat.nr_slab_unreclaimable }, + { .field = "nr_slab", + .offset = &_pm_proc_vmstat.nr_slab }, /* not in later kernels */ + { .field = "nr_unevictable", + .offset = &_pm_proc_vmstat.nr_unevictable }, + { .field = "nr_unstable", + .offset = &_pm_proc_vmstat.nr_unstable }, + { .field = "nr_vmscan_write", + .offset = &_pm_proc_vmstat.nr_vmscan_write }, + { .field = "nr_writeback", + .offset = &_pm_proc_vmstat.nr_writeback }, + { .field = "nr_writeback_temp", + .offset = &_pm_proc_vmstat.nr_writeback_temp }, + { .field = "nr_written", + .offset = &_pm_proc_vmstat.nr_written }, + { .field = "numa_hit", + .offset = &_pm_proc_vmstat.numa_hit }, + { .field = "numa_miss", + .offset = &_pm_proc_vmstat.numa_miss }, + { .field = "numa_foreign", + .offset = &_pm_proc_vmstat.numa_foreign }, + { .field = "numa_interleave", + .offset = &_pm_proc_vmstat.numa_interleave }, + { .field = "numa_local", + .offset = &_pm_proc_vmstat.numa_local }, + { .field = "numa_other", + .offset = &_pm_proc_vmstat.numa_other }, + { .field = "pageoutrun", + .offset = &_pm_proc_vmstat.pageoutrun }, + { .field = "pgactivate", + .offset = &_pm_proc_vmstat.pgactivate }, + { .field = "pgalloc_dma", + .offset = &_pm_proc_vmstat.pgalloc_dma }, + { .field = "pgalloc_dma32", + .offset = &_pm_proc_vmstat.pgalloc_dma32 }, + { .field = "pgalloc_high", + .offset = &_pm_proc_vmstat.pgalloc_high }, + { .field = "pgalloc_movable", + .offset = &_pm_proc_vmstat.pgalloc_movable }, + { .field = "pgalloc_normal", + .offset = &_pm_proc_vmstat.pgalloc_normal }, + { .field = "pgdeactivate", + .offset = &_pm_proc_vmstat.pgdeactivate }, + { .field = "pgfault", + .offset = &_pm_proc_vmstat.pgfault }, + { .field = "pgfree", + .offset = &_pm_proc_vmstat.pgfree }, + { .field = "pginodesteal", + .offset = &_pm_proc_vmstat.pginodesteal }, + { .field = "pgmajfault", + .offset = &_pm_proc_vmstat.pgmajfault }, + { .field = "pgpgin", + .offset = &_pm_proc_vmstat.pgpgin }, + { .field = "pgpgout", + .offset = &_pm_proc_vmstat.pgpgout }, + { .field = "pgrefill_dma", + .offset = &_pm_proc_vmstat.pgrefill_dma }, + { .field = "pgrefill_dma32", + .offset = &_pm_proc_vmstat.pgrefill_dma32 }, + { .field = "pgrefill_high", + .offset = &_pm_proc_vmstat.pgrefill_high }, + { .field = "pgrefill_movable", + .offset = &_pm_proc_vmstat.pgrefill_movable }, + { .field = "pgrefill_normal", + .offset = &_pm_proc_vmstat.pgrefill_normal }, + { .field = "pgrotated", + .offset = &_pm_proc_vmstat.pgrotated }, + { .field = "pgscan_direct_dma", + .offset = &_pm_proc_vmstat.pgscan_direct_dma }, + { .field = "pgscan_direct_dma32", + .offset = &_pm_proc_vmstat.pgscan_direct_dma32 }, + { .field = "pgscan_direct_high", + .offset = &_pm_proc_vmstat.pgscan_direct_high }, + { .field = "pgscan_direct_movable", + .offset = &_pm_proc_vmstat.pgscan_direct_movable }, + { .field = "pgscan_direct_normal", + .offset = &_pm_proc_vmstat.pgscan_direct_normal }, + { .field = "pgscan_kswapd_dma", + .offset = &_pm_proc_vmstat.pgscan_kswapd_dma }, + { .field = "pgscan_kswapd_dma32", + .offset = &_pm_proc_vmstat.pgscan_kswapd_dma32 }, + { .field = "pgscan_kswapd_high", + .offset = &_pm_proc_vmstat.pgscan_kswapd_high }, + { .field = "pgscan_kswapd_movable", + .offset = &_pm_proc_vmstat.pgscan_kswapd_movable }, + { .field = "pgscan_kswapd_normal", + .offset = &_pm_proc_vmstat.pgscan_kswapd_normal }, + { .field = "pgsteal_dma", + .offset = &_pm_proc_vmstat.pgsteal_dma }, + { .field = "pgsteal_dma32", + .offset = &_pm_proc_vmstat.pgsteal_dma32 }, + { .field = "pgsteal_high", + .offset = &_pm_proc_vmstat.pgsteal_high }, + { .field = "pgsteal_movable", + .offset = &_pm_proc_vmstat.pgsteal_movable }, + { .field = "pgsteal_normal", + .offset = &_pm_proc_vmstat.pgsteal_normal }, + { .field = "pswpin", + .offset = &_pm_proc_vmstat.pswpin }, + { .field = "pswpout", + .offset = &_pm_proc_vmstat.pswpout }, + { .field = "slabs_scanned", + .offset = &_pm_proc_vmstat.slabs_scanned }, + { .field = "thp_collapse_alloc", + .offset = &_pm_proc_vmstat.thp_collapse_alloc }, + { .field = "thp_collapse_alloc_failed", + .offset = &_pm_proc_vmstat.thp_collapse_alloc_failed }, + { .field = "thp_fault_alloc", + .offset = &_pm_proc_vmstat.thp_fault_alloc }, + { .field = "thp_fault_fallback", + .offset = &_pm_proc_vmstat.thp_fault_fallback }, + { .field = "thp_split", + .offset = &_pm_proc_vmstat.thp_split }, + { .field = "unevictable_pgs_cleared", + .offset = &_pm_proc_vmstat.unevictable_pgs_cleared }, + { .field = "unevictable_pgs_culled", + .offset = &_pm_proc_vmstat.unevictable_pgs_culled }, + { .field = "unevictable_pgs_mlocked", + .offset = &_pm_proc_vmstat.unevictable_pgs_mlocked }, + { .field = "unevictable_pgs_mlockfreed", + .offset = &_pm_proc_vmstat.unevictable_pgs_mlockfreed }, + { .field = "unevictable_pgs_munlocked", + .offset = &_pm_proc_vmstat.unevictable_pgs_munlocked }, + { .field = "unevictable_pgs_rescued", + .offset = &_pm_proc_vmstat.unevictable_pgs_rescued }, + { .field = "unevictable_pgs_scanned", + .offset = &_pm_proc_vmstat.unevictable_pgs_scanned }, + { .field = "unevictable_pgs_stranded", + .offset = &_pm_proc_vmstat.unevictable_pgs_stranded }, + { .field = "zone_reclaim_failed", + .offset = &_pm_proc_vmstat.zone_reclaim_failed }, + + { .field = NULL, .offset = NULL } +}; + +#define VMSTAT_OFFSET(ii, pp) (int64_t *)((char *)pp + \ + (__psint_t)vmstat_fields[ii].offset - (__psint_t)&_pm_proc_vmstat) + +void +proc_vmstat_init(void) +{ + char buf[1024]; + + /* + * The swap metrics moved from /proc/stat to /proc/vmstat early in 2.6. + * In addition, the swap operation count was removed; the fetch routine + * needs to deal with these quirks and return something sensible based + * (initially) on whether the vmstat file exists. + * + * We'll re-evaluate this on each fetch of the mem.vmstat metrics, but + * that is not a problem. This routine makes sure any swap.xxx metric + * fetch without a preceding mem.vmstat fetch has the correct state. + */ + snprintf(buf, sizeof(buf), "%s/proc/vmstat", linux_statspath); + _pm_have_proc_vmstat = (access(buf, R_OK) == 0); +} + +int +refresh_proc_vmstat(proc_vmstat_t *proc_vmstat) +{ + char buf[1024]; + char *bufp; + int64_t *p; + int i; + FILE *fp; + + for (i = 0; vmstat_fields[i].field != NULL; i++) { + p = VMSTAT_OFFSET(i, proc_vmstat); + *p = -1; /* marked as "no value available" */ + } + + if ((fp = linux_statsfile("/proc/vmstat", buf, sizeof(buf))) == NULL) + return -oserror(); + + _pm_have_proc_vmstat = 1; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((bufp = strchr(buf, ' ')) == NULL) + continue; + *bufp = '\0'; + for (i = 0; vmstat_fields[i].field != NULL; i++) { + if (strcmp(buf, vmstat_fields[i].field) != 0) + continue; + p = VMSTAT_OFFSET(i, proc_vmstat); + for (bufp++; *bufp; bufp++) { + if (isdigit((int)*bufp)) { + sscanf(bufp, "%llu", (unsigned long long *)p); + break; + } + } + } + } + fclose(fp); + + if (proc_vmstat->nr_slab == -1) /* split apart in 2.6.18 */ + proc_vmstat->nr_slab = proc_vmstat->nr_slab_reclaimable + + proc_vmstat->nr_slab_unreclaimable; + return 0; +} diff --git a/src/pmdas/linux/proc_vmstat.h b/src/pmdas/linux/proc_vmstat.h new file mode 100644 index 0000000..adeac39 --- /dev/null +++ b/src/pmdas/linux/proc_vmstat.h @@ -0,0 +1,131 @@ +/* + * Linux /proc/vmstat metrics cluster + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2007,2011 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * All fields (sorted!) in /proc/vmstat for 2.6.x + */ +typedef struct { + /* sorted by name to make maintenance easier */ + __uint64_t allocstall; + __uint64_t compact_blocks_moved; + __uint64_t compact_fail; + __uint64_t compact_pagemigrate_failed; + __uint64_t compact_pages_moved; + __uint64_t compact_stall; + __uint64_t compact_success; + __uint64_t htlb_buddy_alloc_fail; + __uint64_t htlb_buddy_alloc_success; + __uint64_t kswapd_high_wmark_hit_quickly; + __uint64_t kswapd_inodesteal; + __uint64_t kswapd_low_wmark_hit_quickly; + __uint64_t kswapd_skip_congestion_wait; + __uint64_t kswapd_steal; + __uint64_t nr_active_anon; + __uint64_t nr_active_file; + __uint64_t nr_anon_pages; + __uint64_t nr_anon_transparent_hugepages; + __uint64_t nr_bounce; + __uint64_t nr_dirtied; + __uint64_t nr_dirty; + __uint64_t nr_dirty_threshold; + __uint64_t nr_dirty_background_threshold; + __uint64_t nr_file_pages; + __uint64_t nr_free_pages; + __uint64_t nr_inactive_anon; + __uint64_t nr_inactive_file; + __uint64_t nr_isolated_anon; + __uint64_t nr_isolated_file; + __uint64_t nr_kernel_stack; + __uint64_t nr_mapped; + __uint64_t nr_mlock; + __uint64_t nr_page_table_pages; + __uint64_t nr_shmem; + __uint64_t nr_slab; /* not in later kernels */ + __uint64_t nr_slab_reclaimable; + __uint64_t nr_slab_unreclaimable; + __uint64_t nr_unevictable; + __uint64_t nr_unstable; + __uint64_t nr_vmscan_write; + __uint64_t nr_writeback; + __uint64_t nr_writeback_temp; + __uint64_t nr_written; + __uint64_t numa_foreign; + __uint64_t numa_hit; + __uint64_t numa_interleave; + __uint64_t numa_local; + __uint64_t numa_miss; + __uint64_t numa_other; + __uint64_t pageoutrun; + __uint64_t pgactivate; + __uint64_t pgalloc_dma; + __uint64_t pgalloc_dma32; + __uint64_t pgalloc_movable; + __uint64_t pgalloc_high; + __uint64_t pgalloc_normal; + __uint64_t pgdeactivate; + __uint64_t pgfault; + __uint64_t pgfree; + __uint64_t pginodesteal; + __uint64_t pgmajfault; + __uint64_t pgpgin; + __uint64_t pgpgout; + __uint64_t pgrefill_dma; + __uint64_t pgrefill_dma32; + __uint64_t pgrefill_high; + __uint64_t pgrefill_movable; + __uint64_t pgrefill_normal; + __uint64_t pgrotated; + __uint64_t pgscan_direct_dma; + __uint64_t pgscan_direct_dma32; + __uint64_t pgscan_direct_high; + __uint64_t pgscan_direct_movable; + __uint64_t pgscan_direct_normal; + __uint64_t pgscan_kswapd_dma; + __uint64_t pgscan_kswapd_dma32; + __uint64_t pgscan_kswapd_high; + __uint64_t pgscan_kswapd_movable; + __uint64_t pgscan_kswapd_normal; + __uint64_t pgsteal_dma; + __uint64_t pgsteal_dma32; + __uint64_t pgsteal_high; + __uint64_t pgsteal_movable; + __uint64_t pgsteal_normal; + __uint64_t pswpin; + __uint64_t pswpout; + __uint64_t slabs_scanned; + __uint64_t thp_fault_alloc; + __uint64_t thp_fault_fallback; + __uint64_t thp_collapse_alloc; + __uint64_t thp_collapse_alloc_failed; + __uint64_t thp_split; + __uint64_t unevictable_pgs_cleared; + __uint64_t unevictable_pgs_culled; + __uint64_t unevictable_pgs_mlocked; + __uint64_t unevictable_pgs_mlockfreed; + __uint64_t unevictable_pgs_munlocked; + __uint64_t unevictable_pgs_rescued; + __uint64_t unevictable_pgs_scanned; + __uint64_t unevictable_pgs_stranded; + __uint64_t zone_reclaim_failed; +} proc_vmstat_t; + +extern void proc_vmstat_init(void); +extern int refresh_proc_vmstat(proc_vmstat_t *); +extern int _pm_have_proc_vmstat; +extern proc_vmstat_t _pm_proc_vmstat; + diff --git a/src/pmdas/linux/root_linux b/src/pmdas/linux/root_linux new file mode 100644 index 0000000..d66da28 --- /dev/null +++ b/src/pmdas/linux/root_linux @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2000,2004,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * Portions Copyright (c) International Business Machines Corp., 2002 + * Portions Copyright (c) 2007-2009 Aconex. All Rights Reserved. + * Portions Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +root { + hinv + kernel + mem + swap + network + disk + filesys + swapdev + rpc + nfs + nfs3 + nfs4 + pmda + ipc + vfs + tmpfs + sysfs +} + +hinv { + physmem 60:1:9 + pagesize 60:1:11 + ncpu 60:0:32 + ndisk 60:0:33 + nfilesys 60:5:0 + ninterface 60:3:27 + nlv 60:52:1 + nnode 60:0:19 + machine 60:18:7 + map + cpu +} + +hinv.map { + scsi 60:15:0 + cpu_num 60:18:6 + cpu_node 60:18:8 + lvname 60:52:0 + dmname 60:54:13 +} + +hinv.cpu { + clock 60:18:0 + vendor 60:18:1 + model 60:18:2 + stepping 60:18:3 + cache 60:18:4 + bogomips 60:18:5 + model_name 60:18:9 + flags 60:18:10 + cache_alignment 60:18:11 +} + +kernel { + all + percpu + pernode + uname +} + +kernel.all { + cpu + load 60:2:0 + intr 60:0:12 + pswitch 60:0:13 + sysfork 60:0:14 + hz 60:0:48 + uptime 60:26:0 + idletime 60:26:1 + nusers 60:25:0 + lastpid 60:2:1 + runnable 60:2:2 + nprocs 60:2:3 + interrupts +} + +kernel.all.interrupts { + errors 60:4:3 +} + +kernel.all.cpu { + user 60:0:20 + nice 60:0:21 + sys 60:0:22 + idle 60:0:23 + intr 60:0:34 + wait + irq + steal 60:0:55 + guest 60:0:60 + vuser 60:0:78 +} + +kernel.all.cpu.wait { + total 60:0:35 +} + +kernel.all.cpu.irq { + soft 60:0:53 + hard 60:0:54 +} + +kernel.percpu { + interrupts 60:*:* + cpu +} + +kernel.percpu.cpu { + user 60:0:0 + nice 60:0:1 + sys 60:0:2 + idle 60:0:3 + intr 60:0:31 + wait + irq + steal 60:0:58 + guest 60:0:61 + vuser 60:0:76 +} + +kernel.percpu.cpu.wait { + total 60:0:30 +} + +kernel.percpu.cpu.irq { + soft 60:0:56 + hard 60:0:57 +} + +kernel.pernode { + cpu +} + +kernel.pernode.cpu { + user 60:0:62 + nice 60:0:63 + sys 60:0:64 + idle 60:0:65 + intr 60:0:66 + wait + irq + steal 60:0:67 + guest 60:0:68 + vuser 60:0:77 +} + +kernel.pernode.cpu.wait { + total 60:0:69 +} + +kernel.pernode.cpu.irq { + soft 60:0:70 + hard 60:0:71 +} + +kernel.uname { + release 60:12:0 + version 60:12:1 + sysname 60:12:2 + machine 60:12:3 + nodename 60:12:4 + distro 60:12:7 +} + +ipc { + sem + msg + shm +} + +ipc.sem { + max_semmap 60:21:0 + max_semid 60:21:1 + max_sem 60:21:2 + num_undo 60:21:3 + max_perid 60:21:4 + max_ops 60:21:5 + max_undoent 60:21:6 + sz_semundo 60:21:7 + max_semval 60:21:8 + max_exit 60:21:9 +} + +ipc.msg { + sz_pool 60:22:0 + mapent 60:22:1 + max_msgsz 60:22:2 + max_defmsgq 60:22:3 + max_msgqid 60:22:4 + max_msgseg 60:22:5 + num_smsghdr 60:22:6 + max_seg 60:22:7 +} + +ipc.shm { + max_segsz 60:23:0 + min_segsz 60:23:1 + max_seg 60:23:2 + max_segproc 60:23:3 + max_shmsys 60:23:4 +} + +pmda { + uname 60:12:5 + version 60:12:6 +} + +disk { + dev + all + partitions + dm +} + +disk.dev { + read 60:0:4 + write 60:0:5 + total 60:0:28 + blkread 60:0:6 + blkwrite 60:0:7 + blktotal 60:0:36 + read_bytes 60:0:38 + write_bytes 60:0:39 + total_bytes 60:0:40 + read_merge 60:0:49 + write_merge 60:0:50 + avactive 60:0:46 + read_rawactive 60:0:72 + write_rawactive 60:0:73 + aveq 60:0:47 + scheduler 60:0:59 +} + +disk.all { + read 60:0:24 + write 60:0:25 + total 60:0:29 + blkread 60:0:26 + blkwrite 60:0:27 + blktotal 60:0:37 + read_bytes 60:0:41 + write_bytes 60:0:42 + total_bytes 60:0:43 + read_merge 60:0:51 + write_merge 60:0:52 + avactive 60:0:44 + read_rawactive 60:0:74 + write_rawactive 60:0:75 + aveq 60:0:45 +} + +disk.dm { + read 60:54:0 + write 60:54:1 + total 60:54:2 + blkread 60:54:3 + blkwrite 60:54:4 + blktotal 60:54:5 + read_bytes 60:54:6 + write_bytes 60:54:7 + total_bytes 60:54:8 + read_merge 60:54:9 + write_merge 60:54:10 + avactive 60:54:11 + aveq 60:54:12 + read_rawactive 60:54:14 + write_rawactive 60:54:15 +} + +disk.partitions { + read 60:10:0 + write 60:10:1 + total 60:10:2 + blkread 60:10:3 + blkwrite 60:10:4 + blktotal 60:10:5 + read_bytes 60:10:6 + write_bytes 60:10:7 + total_bytes 60:10:8 +} + +mem { + physmem 60:1:0 + freemem 60:1:10 + util + numa + slabinfo + vmstat +} + +mem.util { + used 60:1:1 + free 60:1:2 + shared 60:1:3 + bufmem 60:1:4 + cached 60:1:5 + other 60:1:12 + swapCached 60:1:13 + active 60:1:14 + inactive 60:1:15 + highTotal 60:1:16 + highFree 60:1:17 + lowTotal 60:1:18 + lowFree 60:1:19 + swapTotal 60:1:20 + swapFree 60:1:21 + dirty 60:1:22 + writeback 60:1:23 + mapped 60:1:24 + slab 60:1:25 + committed_AS 60:1:26 + pageTables 60:1:27 + reverseMaps 60:1:28 + cache_clean 60:1:29 + anonpages 60:1:30 + commitLimit 60:1:31 + bounce 60:1:32 + NFS_Unstable 60:1:33 + slabReclaimable 60:1:34 + slabUnreclaimable 60:1:35 + active_anon 60:1:36 + inactive_anon 60:1:37 + active_file 60:1:38 + inactive_file 60:1:39 + unevictable 60:1:40 + mlocked 60:1:41 + shmem 60:1:42 + kernelStack 60:1:43 + hugepagesTotal 60:1:44 + hugepagesFree 60:1:45 + hugepagesRsvd 60:1:46 + hugepagesSurp 60:1:47 + directMap4k 60:1:48 + directMap2M 60:1:49 + vmallocTotal 60:1:50 + vmallocUsed 60:1:51 + vmallocChunk 60:1:52 + mmap_copy 60:1:53 + quicklists 60:1:54 + corrupthardware 60:1:55 + anonhugepages 60:1:56 + directMap1G 60:1:57 + available 60:1:58 +} + +mem.numa { + util + alloc +} + +mem.numa.util { + total 60:36:0 + free 60:36:1 + used 60:36:2 + active 60:36:3 + inactive 60:36:4 + active_anon 60:36:5 + inactive_anon 60:36:6 + active_file 60:36:7 + inactive_file 60:36:8 + highTotal 60:36:9 + highFree 60:36:10 + lowTotal 60:36:11 + lowFree 60:36:12 + unevictable 60:36:13 + mlocked 60:36:14 + dirty 60:36:15 + writeback 60:36:16 + filePages 60:36:17 + mapped 60:36:18 + anonpages 60:36:19 + shmem 60:36:20 + kernelStack 60:36:21 + pageTables 60:36:22 + NFS_Unstable 60:36:23 + bounce 60:36:24 + writebackTmp 60:36:25 + slab 60:36:26 + slabReclaimable 60:36:27 + slabUnreclaimable 60:36:28 + hugepagesTotal 60:36:29 + hugepagesFree 60:36:30 + hugepagesSurp 60:36:31 +} + +mem.numa.alloc { + hit 60:36:32 + miss 60:36:33 + foreign 60:36:34 + interleave_hit 60:36:35 + local_node 60:36:36 + other_node 60:36:37 +} + +swap { + pagesin 60:0:8 + pagesout 60:0:9 + in 60:0:10 + out 60:0:11 + free 60:1:8 + length 60:1:6 + used 60:1:7 +} + +network { + interface + sockstat + ip + icmp + icmpmsg + tcp + udp + udplite + tcpconn +} + +network.interface { + collisions 60:3:13 + in + out + total + mtu 60:3:21 + speed 60:3:22 + baudrate 60:3:23 + duplex 60:3:24 + up 60:3:25 + running 60:3:26 + inet_addr 60:33:0 + ipv6_addr 60:33:1 + ipv6_scope 60:33:2 + hw_addr 60:33:3 +} + +network.interface.in { + bytes 60:3:0 + packets 60:3:1 + errors 60:3:2 + drops 60:3:3 + fifo 60:3:4 + frame 60:3:5 + compressed 60:3:6 + mcasts 60:3:7 +} + +network.interface.out { + bytes 60:3:8 + packets 60:3:9 + errors 60:3:10 + drops 60:3:11 + fifo 60:3:12 + carrier 60:3:14 + compressed 60:3:15 +} + +network.interface.total { + bytes 60:3:16 + packets 60:3:17 + errors 60:3:18 + drops 60:3:19 + mcasts 60:3:20 +} + +network.sockstat { + tcp + udp + raw +} + +network.sockstat.tcp { + inuse 60:11:0 + highest 60:11:1 + util 60:11:2 +} + +network.sockstat.udp { + inuse 60:11:3 + highest 60:11:4 + util 60:11:5 +} + +network.sockstat.raw { + inuse 60:11:6 + highest 60:11:7 + util 60:11:8 +} + +network.ip { + forwarding 60:14:00 + defaultttl 60:14:01 + inreceives 60:14:02 + inhdrerrors 60:14:03 + inaddrerrors 60:14:04 + forwdatagrams 60:14:05 + inunknownprotos 60:14:06 + indiscards 60:14:07 + indelivers 60:14:08 + outrequests 60:14:09 + outdiscards 60:14:10 + outnoroutes 60:14:11 + reasmtimeout 60:14:12 + reasmreqds 60:14:13 + reasmoks 60:14:14 + reasmfails 60:14:15 + fragoks 60:14:16 + fragfails 60:14:17 + fragcreates 60:14:18 + innoroutes 60:53:00 + intruncatedpkts 60:53:01 + inmcastpkts 60:53:02 + outmcastpkts 60:53:03 + inbcastpkts 60:53:04 + outbcastpkts 60:53:05 + inoctets 60:53:06 + outoctets 60:53:07 + inmcastoctets 60:53:08 + outmcastoctets 60:53:09 + inbcastoctets 60:53:10 + outbcastoctets 60:53:11 + csumerrors 60:53:12 + noectpkts 60:53:13 + ect1pkts 60:53:14 + ect0pkts 60:53:15 + cepkts 60:53:16 +} + +network.icmp { + inmsgs 60:14:20 + inerrors 60:14:21 + indestunreachs 60:14:22 + intimeexcds 60:14:23 + inparmprobs 60:14:24 + insrcquenchs 60:14:25 + inredirects 60:14:26 + inechos 60:14:27 + inechoreps 60:14:28 + intimestamps 60:14:29 + intimestampreps 60:14:30 + inaddrmasks 60:14:31 + inaddrmaskreps 60:14:32 + outmsgs 60:14:33 + outerrors 60:14:34 + outdestunreachs 60:14:35 + outtimeexcds 60:14:36 + outparmprobs 60:14:37 + outsrcquenchs 60:14:38 + outredirects 60:14:39 + outechos 60:14:40 + outechoreps 60:14:41 + outtimestamps 60:14:42 + outtimestampreps 60:14:43 + outaddrmasks 60:14:44 + outaddrmaskreps 60:14:45 + incsumerrors 60:14:46 +} + +network.tcp { + rtoalgorithm 60:14:50 + rtomin 60:14:51 + rtomax 60:14:52 + maxconn 60:14:53 + activeopens 60:14:54 + passiveopens 60:14:55 + attemptfails 60:14:56 + estabresets 60:14:57 + currestab 60:14:58 + insegs 60:14:59 + outsegs 60:14:60 + retranssegs 60:14:61 + inerrs 60:14:62 + outrsts 60:14:63 + incsumerrors 60:14:64 + syncookiessent 60:53:17 + syncookiesrecv 60:53:18 + syncookiesfailed 60:53:19 + embryonicrsts 60:53:20 + prunecalled 60:53:21 + rcvpruned 60:53:22 + ofopruned 60:53:23 + outofwindowicmps 60:53:24 + lockdroppedicmps 60:53:25 + arpfilter 60:53:26 + timewaited 60:53:27 + timewaitrecycled 60:53:28 + timewaitkilled 60:53:29 + pawspassiverejected 60:53:30 + pawsactiverejected 60:53:31 + pawsestabrejected 60:53:32 + delayedacks 60:53:33 + delayedacklocked 60:53:34 + delayedacklost 60:53:35 + listenoverflows 60:53:36 + listendrops 60:53:37 + prequeued 60:53:38 + directcopyfrombacklog 60:53:39 + directcopyfromprequeue 60:53:40 + prequeueddropped 60:53:41 + hphits 60:53:42 + hphitstouser 60:53:43 + pureacks 60:53:44 + hpacks 60:53:45 + renorecovery 60:53:46 + sackrecovery 60:53:47 + sackreneging 60:53:48 + fackreorder 60:53:49 + sackreorder 60:53:50 + renoreorder 60:53:51 + tsreorder 60:53:52 + fullundo 60:53:53 + partialundo 60:53:54 + dsackundo 60:53:55 + lossundo 60:53:56 + lostretransmit 60:53:57 + renofailures 60:53:58 + sackfailures 60:53:59 + lossfailures 60:53:60 + fastretrans 60:53:61 + forwardretrans 60:53:62 + slowstartretrans 60:53:63 + timeouts 60:53:64 + lossprobes 60:53:65 + lossproberecovery 60:53:66 + renorecoveryfail 60:53:67 + sackrecoveryfail 60:53:68 + schedulerfail 60:53:69 + rcvcollapsed 60:53:70 + dsackoldsent 60:53:71 + dsackofosent 60:53:72 + dsackrecv 60:53:73 + dsackoforecv 60:53:74 + abortondata 60:53:75 + abortonclose 60:53:76 + abortonmemory 60:53:77 + abortontimeout 60:53:78 + abortonlinger 60:53:79 + abortfailed 60:53:80 + memorypressures 60:53:81 + sackdiscard 60:53:82 + dsackignoredold 60:53:83 + dsackignorednoundo 60:53:84 + spuriousrtos 60:53:85 + md5notfound 60:53:86 + md5unexpected 60:53:87 + sackshifted 60:53:88 + sackmerged 60:53:89 + sackshiftfallback 60:53:90 + backlogdrop 60:53:91 + minttldrop 60:53:92 + deferacceptdrop 60:53:93 + iprpfilter 60:53:94 + timewaitoverflow 60:53:95 + reqqfulldocookies 60:53:96 + reqqfulldrop 60:53:97 + retransfail 60:53:98 + rcvcoalesce 60:53:99 + ofoqueue 60:53:100 + ofodrop 60:53:101 + ofomerge 60:53:102 + challengeack 60:53:103 + synchallenge 60:53:104 + fastopenactive 60:53:105 + fastopenactivefail 60:53:106 + fastopenpassive 60:53:107 + fastopenpassivefail 60:53:108 + fastopenlistenoverflow 60:53:109 + fastopencookiereqd 60:53:110 + spuriousrtxhostqueues 60:53:111 + busypollrxpackets 60:53:112 + autocorking 60:53:113 + fromzerowindowadv 60:53:114 + tozerowindowadv 60:53:115 + wantzerowindowadv 60:53:116 + synretrans 60:53:117 + origdatasent 60:53:118 +} + +network.udp { + indatagrams 60:14:70 + noports 60:14:71 + inerrors 60:14:72 + outdatagrams 60:14:74 + recvbuferrors 60:14:75 + sndbuferrors 60:14:76 + incsumerrors 60:14:83 +} + +network.udplite { + indatagrams 60:14:77 + noports 60:14:78 + inerrors 60:14:79 + outdatagrams 60:14:80 + recvbuferrors 60:14:81 + sndbuferrors 60:14:82 + incsumerrors 60:14:84 +} + +network.icmpmsg { + intype 60:14:88 + outtype 60:14:89 +} + +filesys { + capacity 60:5:1 + used 60:5:2 + free 60:5:3 + maxfiles 60:5:4 + usedfiles 60:5:5 + freefiles 60:5:6 + mountdir 60:5:7 + full 60:5:8 + blocksize 60:5:9 + avail 60:5:10 + readonly 60:5:11 +} + +tmpfs { + capacity 60:34:1 + used 60:34:2 + free 60:34:3 + maxfiles 60:34:4 + usedfiles 60:34:5 + freefiles 60:34:6 + full 60:34:7 +} + +swapdev { + free 60:6:0 + length 60:6:1 + maxswap 60:6:2 + vlength 60:6:3 + priority 60:6:4 +} + +nfs { + client + server +} + +nfs.client { + calls 60:7:1 + reqs 60:7:4 +} + +nfs.server { + calls 60:7:50 + reqs 60:7:12 +} + +rpc { + client + server +} + +rpc.client { + rpccnt 60:7:20 + rpcretrans 60:7:21 + rpcauthrefresh 60:7:22 + netcnt 60:7:24 + netudpcnt 60:7:25 + nettcpcnt 60:7:26 + nettcpconn 60:7:27 +} + +rpc.server { + rpccnt 60:7:30 + rpcerr 60:7:31 + rpcbadfmt 60:7:32 + rpcbadauth 60:7:33 + rpcbadclnt 60:7:34 + rchits 60:7:35 + rcmisses 60:7:36 + rcnocache 60:7:37 + fh_cached 60:7:38 + fh_valid 60:7:39 + fh_fixup 60:7:40 + fh_lookup 60:7:41 + fh_stale 60:7:42 + fh_concurrent 60:7:43 + netcnt 60:7:44 + netudpcnt 60:7:45 + nettcpcnt 60:7:46 + nettcpconn 60:7:47 + fh_anon 60:7:51 + fh_nocache_dir 60:7:52 + fh_nocache_nondir 60:7:53 + io_read 60:7:54 + io_write 60:7:55 + th_cnt 60:7:56 + th_fullcnt 60:7:57 +} + +nfs3 { + client + server +} + +nfs3.client { + calls 60:7:60 + reqs 60:7:61 +} + +nfs3.server { + calls 60:7:62 + reqs 60:7:63 +} + +nfs4 { + client + server +} + +nfs4.client { + calls 60:7:64 + reqs 60:7:65 +} + +nfs4.server { + calls 60:7:66 + reqs 60:7:67 +} + +network.tcpconn { + established 60:19:1 + syn_sent 60:19:2 + syn_recv 60:19:3 + fin_wait1 60:19:4 + fin_wait2 60:19:5 + time_wait 60:19:6 + close 60:19:7 + close_wait 60:19:8 + last_ack 60:19:9 + listen 60:19:10 + closing 60:19:11 +} + +mem.slabinfo { + objects + slabs +} + +mem.slabinfo.objects { + active 60:20:0 + total 60:20:1 + size 60:20:2 +} + +mem.slabinfo.slabs { + active 60:20:3 + total 60:20:4 + pages_per_slab 60:20:5 + objects_per_slab 60:20:6 + total_size 60:20:7 +} + +mem.vmstat { + /* sorted by name to make maintenance easier */ + allocstall 60:28:35 + compact_blocks_moved 60:28:57 + compact_fail 60:28:58 + compact_pagemigrate_failed 60:28:59 + compact_pages_moved 60:28:60 + compact_stall 60:28:61 + compact_success 60:28:62 + htlb_buddy_alloc_fail 60:28:43 + htlb_buddy_alloc_success 60:28:44 + kswapd_inodesteal 60:28:33 + kswapd_low_wmark_hit_quickly 60:28:87 + kswapd_high_wmark_hit_quickly 60:28:88 + kswapd_skip_congestion_wait 60:28:89 + kswapd_steal 60:28:32 + nr_active_anon 60:28:45 + nr_active_file 60:28:46 + nr_anon_pages 60:28:39 + nr_anon_transparent_hugepages 60:28:90 + nr_bounce 60:28:40 + nr_dirtied 60:28:91 + nr_dirty 60:28:0 + nr_dirty_background_threshold 60:28:92 + nr_dirty_threshold 60:28:93 + nr_free_pages 60:28:47 + nr_inactive_anon 60:28:48 + nr_inactive_file 60:28:49 + nr_isolated_anon 60:28:50 + nr_isolated_file 60:28:51 + nr_kernel_stack 60:28:52 + nr_mapped 60:28:4 + nr_mlock 60:28:53 + nr_page_table_pages 60:28:3 + nr_shmem 60:28:54 + nr_slab 60:28:5 + nr_slab_reclaimable 60:28:37 + nr_slab_unreclaimable 60:28:38 + nr_unevictable 60:28:55 + nr_unstable 60:28:2 + nr_vmscan_write 60:28:42 + nr_writeback 60:28:1 + nr_writeback_temp 60:28:56 + nr_written 60:28:94 + numa_foreign 60:28:95 + numa_hit 60:28:96 + numa_interleave 60:28:97 + numa_local 60:28:98 + numa_miss 60:28:99 + numa_other 60:28:100 + pageoutrun 60:28:34 + pgactivate 60:28:14 + pgalloc_dma 60:28:12 + pgalloc_dma32 60:28:63 + pgalloc_high 60:28:10 + pgalloc_movable 60:28:64 + pgalloc_normal 60:28:11 + pgrefill_dma32 60:28:65 + pgrefill_movable 60:28:66 + pgdeactivate 60:28:15 + pgfault 60:28:16 + pgfree 60:28:13 + pginodesteal 60:28:30 + pgmajfault 60:28:17 + pgpgin 60:28:6 + pgpgout 60:28:7 + pgrefill_dma 60:28:20 + pgrefill_high 60:28:18 + pgrefill_normal 60:28:19 + pgrotated 60:28:36 + pgscan_direct_dma 60:28:29 + pgscan_direct_dma32 60:28:67 + pgscan_direct_high 60:28:27 + pgscan_direct_movable 60:28:68 + pgscan_direct_normal 60:28:28 + pgscan_kswapd_dma 60:28:26 + pgscan_kswapd_dma32 60:28:69 + pgscan_kswapd_high 60:28:24 + pgscan_kswapd_movable 60:28:70 + pgscan_kswapd_normal 60:28:25 + pgsteal_dma 60:28:23 + pgsteal_dma32 60:28:71 + pgsteal_high 60:28:21 + pgsteal_movable 60:28:72 + pgsteal_normal 60:28:22 + pswpin 60:28:8 + pswpout 60:28:9 + slabs_scanned 60:28:31 + thp_fault_alloc 60:28:73 + thp_fault_fallback 60:28:74 + thp_collapse_alloc 60:28:75 + thp_collapse_alloc_failed 60:28:76 + thp_split 60:28:77 + unevictable_pgs_cleared 60:28:78 + unevictable_pgs_culled 60:28:79 + unevictable_pgs_mlocked 60:28:80 + unevictable_pgs_mlockfreed 60:28:81 + unevictable_pgs_munlocked 60:28:82 + unevictable_pgs_rescued 60:28:83 + unevictable_pgs_scanned 60:28:84 + unevictable_pgs_stranded 60:28:85 + zone_reclaim_failed 60:28:86 +} + +vfs { + files + inodes + dentry +} + +vfs.files { + count 60:27:0 + free 60:27:1 + max 60:27:2 +} + +vfs.inodes { + count 60:27:3 + free 60:27:4 +} + +vfs.dentry { + count 60:27:5 + free 60:27:6 +} + +sysfs { + kernel +} + +sysfs.kernel { + uevent_seqnum 60:35:0 +} + diff --git a/src/pmdas/linux/sem_limits.c b/src/pmdas/linux/sem_limits.c new file mode 100644 index 0000000..24e336e --- /dev/null +++ b/src/pmdas/linux/sem_limits.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * This code contributed by Mike Mason + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define __USE_GNU 1 /* required for IPC_INFO define */ +#include +#include + +#include "pmapi.h" +#include "sem_limits.h" + +int +refresh_sem_limits(sem_limits_t *sem_limits) +{ + static int started; + static struct seminfo seminfo; + static union semun arg; + + if (!started) { + started = 1; + memset(sem_limits, 0, sizeof(sem_limits_t)); + arg.array = (unsigned short *) &seminfo; + } + + if (semctl(0, 0, IPC_INFO, arg) < 0) { + return -oserror(); + } + + sem_limits->semmap = seminfo.semmap; + sem_limits->semmni = seminfo.semmni; + sem_limits->semmns = seminfo.semmns; + sem_limits->semmnu = seminfo.semmnu; + sem_limits->semmsl = seminfo.semmsl; + sem_limits->semopm = seminfo.semopm; + sem_limits->semume = seminfo.semume; + sem_limits->semusz = seminfo.semusz; + sem_limits->semvmx = seminfo.semvmx; + sem_limits->semaem = seminfo.semaem; + return 0; +} diff --git a/src/pmdas/linux/sem_limits.h b/src/pmdas/linux/sem_limits.h new file mode 100644 index 0000000..ef0694e --- /dev/null +++ b/src/pmdas/linux/sem_limits.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This code contributed by Mike Mason (mmlnx@us.ibm.com) + */ + +#ifdef _SEM_SEMUN_UNDEFINED +/* glibc 2.1 no longer defines semun, instead it defines + * _SEM_SEMUN_UNDEFINED so users can define semun on their own. + */ +union semun { + int val; + struct semid_ds *buf; + unsigned short int *array; + struct seminfo *__buf; +}; +#endif + +typedef struct { + unsigned int semmap; /* # of entries in semaphore map */ + unsigned int semmni; /* max # of semaphore identifiers */ + unsigned int semmns; /* max # of semaphores in system */ + unsigned int semmnu; /* num of undo structures system wide */ + unsigned int semmsl; /* max num of semaphores per id */ + unsigned int semopm; /* max num of ops per semop call */ + unsigned int semume; /* max num of undo entries per process */ + unsigned int semusz; /* sizeof struct sem_undo */ + unsigned int semvmx; /* semaphore maximum value */ + unsigned int semaem; /* adjust on exit max value */ +} sem_limits_t; + +extern int refresh_sem_limits(sem_limits_t*); + diff --git a/src/pmdas/linux/shm_limits.c b/src/pmdas/linux/shm_limits.c new file mode 100644 index 0000000..e1c80f0 --- /dev/null +++ b/src/pmdas/linux/shm_limits.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * This code contributed by Mike Mason + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define __USE_GNU 1 /* required for IPC_INFO define */ +#include +#include + +#include "pmapi.h" +#include "shm_limits.h" + +int +refresh_shm_limits(shm_limits_t *shm_limits) +{ + static int started; + static struct shminfo shminfo; + + if (!started) { + started = 1; + memset(shm_limits, 0, sizeof(shm_limits_t)); + } + + if (shmctl(0, IPC_INFO, (struct shmid_ds *) &shminfo) < 0) + return -oserror(); + + shm_limits->shmmax = shminfo.shmmax; + shm_limits->shmmin = shminfo.shmmin; + shm_limits->shmmni = shminfo.shmmni; + shm_limits->shmseg = shminfo.shmseg; + shm_limits->shmall = shminfo.shmall; + return 0; +} diff --git a/src/pmdas/linux/shm_limits.h b/src/pmdas/linux/shm_limits.h new file mode 100644 index 0000000..39d7f71 --- /dev/null +++ b/src/pmdas/linux/shm_limits.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This code contributed by Mike Mason (mmlnx@us.ibm.com) + */ + +typedef struct { + unsigned int shmmax; /* maximum shared segment size (bytes) */ + unsigned int shmmin; /* minimum shared segment size (bytes) */ + unsigned int shmmni; /* maximum number of segments system wide */ + unsigned int shmseg; /* maximum shared segments per process */ + unsigned int shmall; /* maximum shared memory system wide (pages) */ +} shm_limits_t; + +extern int refresh_shm_limits(shm_limits_t *); + diff --git a/src/pmdas/linux/swapdev.c b/src/pmdas/linux/swapdev.c new file mode 100644 index 0000000..0b5521e --- /dev/null +++ b/src/pmdas/linux/swapdev.c @@ -0,0 +1,72 @@ +/* + * Linux Swap Device Cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "swapdev.h" + +int +refresh_swapdev(pmInDom swapdev_indom) +{ + char buf[MAXPATHLEN]; + swapdev_t *swap; + FILE *fp; + char *path; + char *size; + char *used; + char *priority; + int sts; + + pmdaCacheOp(swapdev_indom, PMDA_CACHE_INACTIVE); + + if ((fp = linux_statsfile("/proc/swaps", buf, sizeof(buf))) == NULL) + return -oserror(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (buf[0] != '/') + continue; + if ((path = strtok(buf, " \t")) == 0) + continue; + if ((/*type: */ strtok(NULL, " \t")) == NULL || + (size = strtok(NULL, " \t")) == NULL || + (used = strtok(NULL, " \t")) == NULL || + (priority = strtok(NULL, " \t")) == NULL) + continue; + sts = pmdaCacheLookupName(swapdev_indom, path, NULL, (void **)&swap); + if (sts == PMDA_CACHE_ACTIVE) /* repeated line in /proc/swaps? */ + continue; + if (sts == PMDA_CACHE_INACTIVE) { /* re-activate an old swap device */ + pmdaCacheStore(swapdev_indom, PMDA_CACHE_ADD, path, swap); + } + else { /* new swap device */ + if ((swap = malloc(sizeof(swapdev_t))) == NULL) + continue; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, "refresh_swapdev: add \"%s\"\n", path); +#endif + pmdaCacheStore(swapdev_indom, PMDA_CACHE_ADD, path, swap); + } + sscanf(size, "%u", &swap->size); + sscanf(used, "%u", &swap->used); + sscanf(priority, "%d", &swap->priority); + } + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux/swapdev.h b/src/pmdas/linux/swapdev.h new file mode 100644 index 0000000..fa619a8 --- /dev/null +++ b/src/pmdas/linux/swapdev.h @@ -0,0 +1,28 @@ +/* + * Linux swap device Cluster + * + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct swapdev { + char *path; + unsigned int size; + unsigned int used; + int priority; +} swapdev_t; + +extern int refresh_swapdev(pmInDom); diff --git a/src/pmdas/linux/sysfs_kernel.c b/src/pmdas/linux/sysfs_kernel.c new file mode 100644 index 0000000..8880634 --- /dev/null +++ b/src/pmdas/linux/sysfs_kernel.c @@ -0,0 +1,41 @@ +/* + * Linux sysfs_kernel cluster + * + * Copyright (c) 2009,2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "sysfs_kernel.h" +#include "indom.h" + +int +refresh_sysfs_kernel(sysfs_kernel_t *sk) +{ + char buf[MAXPATHLEN]; + int fd, n; + + snprintf(buf, sizeof(buf), "%s/sys/kernel/uevent_seqnum", linux_statspath); + if ((fd = open(buf, O_RDONLY)) < 0) { + sk->valid_uevent_seqnum = 0; + return -oserror(); + } + + if ((n = read(fd, buf, sizeof(buf))) <= 0) + sk->valid_uevent_seqnum = 0; + else { + buf[n-1] = '\0'; + sscanf(buf, "%llu", (long long unsigned int *)&sk->uevent_seqnum); + sk->valid_uevent_seqnum = 1; + } + close(fd); + return 0; +} diff --git a/src/pmdas/linux/sysfs_kernel.h b/src/pmdas/linux/sysfs_kernel.h new file mode 100644 index 0000000..b43445d --- /dev/null +++ b/src/pmdas/linux/sysfs_kernel.h @@ -0,0 +1,34 @@ +/* + * Linux sysfs_kernel cluster + * + * Copyright (c) 2009, Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _SYSFS_KERNEL_H +#define _SYSFS_KERNEL_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include + +typedef struct { + int valid_uevent_seqnum; + uint64_t uevent_seqnum; /* /sys/kernel/uevent_seqnum */ + /* TODO queue length, event type counters and other metrics */ +} sysfs_kernel_t; + +/* refresh sysfs_kernel */ +extern int refresh_sysfs_kernel(sysfs_kernel_t *); + +#endif /* _SYSFS_KERNEL_H */ diff --git a/src/pmdas/linux_proc/GNUmakefile b/src/pmdas/linux_proc/GNUmakefile new file mode 100644 index 0000000..97dc518 --- /dev/null +++ b/src/pmdas/linux_proc/GNUmakefile @@ -0,0 +1,89 @@ +# +# Copyright (c) 2000,2003,2004,2008 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2007-2010 Aconex. All Rights Reserved. +# Copyright (c) 2013-2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = proc +DOMAIN = PROC +CMDTARGET = pmdaproc +LIBTARGET = pmda_proc.so +PMDAINIT = proc_init +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "proc 3 pipe binary $(PMDADIR)/$(CMDTARGET) -d 3" + +CFILES = pmda.c \ + cgroups.c proc_pid.c proc_runq.c ksym.c getinfo.c contexts.c + +HFILES = clusters.h indom.h \ + cgroups.h proc_pid.h proc_runq.h ksym.h getinfo.h contexts.h + +SCRIPTS = Install Remove +VERSION_SCRIPT = exports +HELPTARGETS = help.dir help.pag +LSRCFILES = help root root_proc linux_proc_migrate.conf $(SCRIPTS) +LDIRT = $(HELPTARGETS) domain.h $(VERSION_SCRIPT) + +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) + +# Uncomment these flags for profiling +# LCFLAGS += -pg +# LLDFLAGS += -pg + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(LIBTARGET) $(CMDTARGET) $(HELPTARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help help.dir help.pag root root_proc $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 root_proc $(PCP_VAR_DIR)/pmns/root_proc + $(INSTALL) -m 644 linux_proc_migrate.conf $(PCP_VAR_DIR)/config/pmlogrewrite/linux_proc_migrate.conf +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +$(HELPTARGETS) : help + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_proc -v 2 -o help < help + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +cgroups.o pmda.o: clusters.h +cgroups.o pmda.o: cgroups.h +cgroups.o pmda.o proc_pid.o proc_runq.o: proc_pid.h +pmda.o proc_runq.o: proc_runq.h +indom.o pmda.o: indom.h +ksym.o pmda.o: ksym.h +pmda.o: domain.h +pmda.o: getinfo.h +pmda.o: $(VERSION_SCRIPT) diff --git a/src/pmdas/linux_proc/Install b/src/pmdas/linux_proc/Install new file mode 100755 index 0000000..74fa225 --- /dev/null +++ b/src/pmdas/linux_proc/Install @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (c) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Linux per-process (proc) PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=proc +pmda_interface=6 +daemon_opt=true +pipe_opt=true +pmns_source=root_proc + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/linux_proc/Remove b/src/pmdas/linux_proc/Remove new file mode 100755 index 0000000..4befc73 --- /dev/null +++ b/src/pmdas/linux_proc/Remove @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright (c) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Linux per-process (proc) PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh +iam=proc +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/linux_proc/cgroups.c b/src/pmdas/linux_proc/cgroups.c new file mode 100644 index 0000000..4994465 --- /dev/null +++ b/src/pmdas/linux_proc/cgroups.c @@ -0,0 +1,1146 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "cgroups.h" +#include "clusters.h" +#include "proc_pid.h" +#include +#include + +#define CGROUP_ROOT "cgroup.groups" /* root dynamic PMNS node */ + +/* Add namespace entries and prepare values for one cgroupfs directory entry */ +struct cgroup_subsys; +typedef int (*cgroup_prepare_t)(__pmnsTree *, const char *, + struct cgroup_subsys *, const char *, int, int, int); +static int prepare_ull(__pmnsTree *, const char *, + struct cgroup_subsys *, const char *, int, int, int); +static int prepare_string(__pmnsTree *, const char *, + struct cgroup_subsys *, const char *, int, int, int); +static int prepare_named_ull(__pmnsTree *, const char *, + struct cgroup_subsys *, const char *, int, int, int); +static int prepare_block_ull(__pmnsTree *, const char *, + struct cgroup_subsys *, const char *, int, int, int); +static int prepare_blocks_ull(__pmnsTree *, const char *, + struct cgroup_subsys *, const char *, int, int, int); + +/* + * Critical data structures for cgroup subsystem in pmdaproc ... + * Initial comment for each struct talks about lifecycle of that + * data, in terms of what pmdaproc must do with it (esp. memory + * allocation related). + */ + +typedef struct { /* contents depends on individual kernel cgroups */ + int item; /* PMID == domain:cluster:[id:item] */ + int dynamic; /* do we need an extra free (string) */ + cgroup_prepare_t prepare; /* setup metric name(s) and value(s) */ + char *suffix; /* cpus/mems/rss/... */ +} cgroup_metrics_t; + +typedef struct { /* some metrics are multi-valued, but most have only one */ + int item; /* PMID == domain:cluster:[id:item] */ + int atom_count; + pmAtomValue *atoms; +} cgroup_values_t; + +typedef struct { /* contains data for each group users have created, if any */ + int id; /* PMID == domain:cluster:[id:item] */ + int refreshed; /* boolean: are values all uptodate */ + proc_pid_list_t process_list; + cgroup_values_t *metric_values; +} cgroup_group_t; + +typedef struct cgroup_subsys { /* contents covers the known kernel cgroups */ + const char *name; /* cpuset/memory/... */ + int cluster; /* PMID == domain:cluster:[id:item] */ + int group_count; /* number of groups (dynamic) */ + int metric_count; /* number of metrics (fixed) */ + time_t previous_time; /* used to avoid repeated refresh */ + cgroup_group_t *groups; /* array of groups (dynamic) */ + cgroup_metrics_t *metrics; /* array of metrics (fixed) */ +} cgroup_subsys_t; + +static cgroup_metrics_t cpusched_metrics[] = { + { .suffix = "shares", .prepare = prepare_ull }, +}; + +static cgroup_metrics_t cpuacct_metrics[] = { + { .suffix = "stat.user", .prepare = prepare_named_ull }, + { .suffix = "stat.system", .prepare = prepare_named_ull }, + { .suffix = "usage", .prepare = prepare_ull }, + { .suffix = "usage_percpu", .prepare = prepare_ull }, +}; + +static cgroup_metrics_t cpuset_metrics[] = { + { .suffix = "io_merged", .prepare = prepare_string }, + { .suffix = "sectors", .prepare = prepare_string }, +}; + +static cgroup_metrics_t memory_metrics[] = { + { .suffix = "stat.cache", .prepare = prepare_named_ull }, + { .suffix = "stat.rss", .prepare = prepare_named_ull }, + { .suffix = "stat.rss_huge", .prepare = prepare_named_ull }, + { .suffix = "stat.mapped_file", .prepare = prepare_named_ull }, + { .suffix = "stat.writeback", .prepare = prepare_named_ull }, + { .suffix = "stat.swap", .prepare = prepare_named_ull }, + { .suffix = "stat.pgpgin", .prepare = prepare_named_ull }, + { .suffix = "stat.pgpgout", .prepare = prepare_named_ull }, + { .suffix = "stat.pgfault", .prepare = prepare_named_ull }, + { .suffix = "stat.pgmajfault", .prepare = prepare_named_ull }, + { .suffix = "stat.inactive_anon", .prepare = prepare_named_ull }, + { .suffix = "stat.active_anon", .prepare = prepare_named_ull }, + { .suffix = "stat.inactive_file", .prepare = prepare_named_ull }, + { .suffix = "stat.active_file", .prepare = prepare_named_ull }, + { .suffix = "stat.unevictable", .prepare = prepare_named_ull }, + { .suffix = "stat.total_cache", .prepare = prepare_named_ull }, + { .suffix = "stat.total_rss", .prepare = prepare_named_ull }, + { .suffix = "stat.total_rss_huge", .prepare = prepare_named_ull }, + { .suffix = "stat.total_mapped_file", .prepare = prepare_named_ull }, + { .suffix = "stat.total_writeback", .prepare = prepare_named_ull }, + { .suffix = "stat.total_swap", .prepare = prepare_named_ull }, + { .suffix = "stat.total_pgpgin", .prepare = prepare_named_ull }, + { .suffix = "stat.total_pgpgout", .prepare = prepare_named_ull }, + { .suffix = "stat.total_pgfault", .prepare = prepare_named_ull }, + { .suffix = "stat.total_pgmajfault", .prepare = prepare_named_ull }, + { .suffix = "stat.total_inactive_anon", .prepare = prepare_named_ull }, + { .suffix = "stat.total_active_anon", .prepare = prepare_named_ull }, + { .suffix = "stat.total_inactive_file", .prepare = prepare_named_ull }, + { .suffix = "stat.total_active_file", .prepare = prepare_named_ull }, + { .suffix = "stat.total_unevictable", .prepare = prepare_named_ull }, + { .suffix = "stat.recent_rotated_anon", .prepare = prepare_named_ull }, + { .suffix = "stat.recent_rotated_file", .prepare = prepare_named_ull }, + { .suffix = "stat.recent_scanned_anon", .prepare = prepare_named_ull }, + { .suffix = "stat.recent_scanned_file", .prepare = prepare_named_ull }, +}; + +static cgroup_metrics_t netclass_metrics[] = { + { .suffix = "classid", .prepare = prepare_ull }, +}; + +static cgroup_metrics_t blkio_metrics[] = { + { .suffix = "io_merged.read", .prepare = prepare_blocks_ull }, + { .suffix = "io_merged.write", .prepare = prepare_blocks_ull }, + { .suffix = "io_merged.sync", .prepare = prepare_blocks_ull }, + { .suffix = "io_merged.async", .prepare = prepare_blocks_ull }, + { .suffix = "io_merged.total", .prepare = prepare_blocks_ull }, + { .suffix = "io_queued.read", .prepare = prepare_blocks_ull }, + { .suffix = "io_queued.write", .prepare = prepare_blocks_ull }, + { .suffix = "io_queued.sync", .prepare = prepare_blocks_ull }, + { .suffix = "io_queued.async", .prepare = prepare_blocks_ull }, + { .suffix = "io_queued.total", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_bytes.read", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_bytes.write", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_bytes.sync", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_bytes.async", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_bytes.total", .prepare = prepare_blocks_ull }, + { .suffix = "io_serviced.read", .prepare = prepare_blocks_ull }, + { .suffix = "io_serviced.write", .prepare = prepare_blocks_ull }, + { .suffix = "io_serviced.sync", .prepare = prepare_blocks_ull }, + { .suffix = "io_serviced.async", .prepare = prepare_blocks_ull }, + { .suffix = "io_serviced.total", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_time.read", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_time.write", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_time.sync", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_time.async", .prepare = prepare_blocks_ull }, + { .suffix = "io_service_time.total", .prepare = prepare_blocks_ull }, + { .suffix = "io_wait_time.read", .prepare = prepare_blocks_ull }, + { .suffix = "io_wait_time.write", .prepare = prepare_blocks_ull }, + { .suffix = "io_wait_time.sync", .prepare = prepare_blocks_ull }, + { .suffix = "io_wait_time.async", .prepare = prepare_blocks_ull }, + { .suffix = "io_wait_time.total", .prepare = prepare_blocks_ull }, + { .suffix = "sectors", .prepare = prepare_block_ull }, + { .suffix = "time", .prepare = prepare_block_ull }, +}; + +static const char *block_stats_names[] = \ + { "read", "write", "sync", "async", "total" }; +#define BLKIOS (sizeof(block_stats_names)/sizeof(block_stats_names[0])) + +static cgroup_subsys_t controllers[] = { + { .name = "cpu", + .cluster = CLUSTER_CPUSCHED_GROUPS, + .metrics = cpusched_metrics, + .metric_count = sizeof(cpusched_metrics) / sizeof(cgroup_metrics_t), + }, + { .name = "cpuset", + .cluster = CLUSTER_CPUSET_GROUPS, + .metrics = cpuset_metrics, + .metric_count = sizeof(cpuset_metrics) / sizeof(cgroup_metrics_t), + }, + { .name = "cpuacct", + .cluster = CLUSTER_CPUACCT_GROUPS, + .metrics = cpuacct_metrics, + .metric_count = sizeof(cpuacct_metrics) / sizeof(cgroup_metrics_t), + }, + { .name = "memory", + .cluster = CLUSTER_MEMORY_GROUPS, + .metrics = memory_metrics, + .metric_count = sizeof(memory_metrics) / sizeof(cgroup_metrics_t), + }, + { .name = "net_cls", + .cluster = CLUSTER_NET_CLS_GROUPS, + .metrics = netclass_metrics, + .metric_count = sizeof(netclass_metrics) / sizeof(cgroup_metrics_t), + }, + { .name = "blkio", + .cluster = CLUSTER_BLKIO_GROUPS, + .metrics = blkio_metrics, + .metric_count = sizeof(blkio_metrics) / sizeof(cgroup_metrics_t), + }, +}; + +/* + * Data structures used by individual cgroup subsystem controllers + */ +typedef struct { + __uint32_t major; + __uint32_t minor; + int inst; + char *name; +} device_t; + +typedef struct { + device_t dev; + __uint64_t values[BLKIOS]; /* read, write, sync, async, total */ +} block_stats_t; + +typedef struct filesys { + int id; + char *device; + char *path; + char *options; +} filesys_t; + +void +refresh_cgroup_cpus(pmInDom indom) +{ + char buf[MAXPATHLEN]; + char *space; + FILE *fp; + + pmdaCacheOp(indom, PMDA_CACHE_INACTIVE); + if ((fp = proc_statsfile("/proc/stat", buf, sizeof(buf))) == NULL) + return; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "cpu", 3) == 0 && isdigit((int)buf[3])) { + if ((space = strchr(buf, ' ')) != NULL) { + *space = '\0'; + pmdaCacheStore(indom, PMDA_CACHE_ADD, buf, NULL); + } + } + } + fclose(fp); +} + +static int +_pm_isloop(char *dname) +{ + return strncmp(dname, "loop", 4) == 0; +} + +static int +_pm_isramdisk(char *dname) +{ + return strncmp(dname, "ram", 3) == 0; +} + +/* + * For block devices we have one instance domain for dev_t + * based lookup, and another for (real) name lookup. + * The reason we need this is that the blkio cgroup stats + * are exported using the major:minor numbers, and not the + * device names - we must perform that mapping ourselves. + * In some places (value refresh) we need to lookup the blk + * name from device major/minor, in other places (instances + * refresh) we need the usual external instid:name lookup. + */ +void +refresh_cgroup_devices(pmInDom diskindom) +{ + pmInDom devtindom = INDOM(DEVT_INDOM); + char buf[MAXPATHLEN]; + static time_t before; + time_t now; + FILE *fp; + + if ((now = time(NULL)) == before) + return; + before = now; + + pmdaCacheOp(devtindom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(diskindom, PMDA_CACHE_INACTIVE); + + if ((fp = proc_statsfile("/proc/diskstats", buf, sizeof(buf))) == NULL) + return; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + unsigned int major, minor, unused; + device_t *dev = NULL; + char namebuf[1024]; + int inst; + + if (sscanf(buf, "%u %u %s %u", &major, &minor, namebuf, &unused) != 4) + continue; + if (_pm_isloop(namebuf) || _pm_isramdisk(namebuf)) + continue; + if (pmdaCacheLookupName(diskindom, namebuf, &inst, (void **)&dev) < 0 || + dev == NULL) { + if (!(dev = (device_t *)malloc(sizeof(device_t)))) { + __pmNoMem("device", sizeof(device_t), PM_RECOV_ERR); + continue; + } + dev->major = major; + dev->minor = minor; + } + /* keeping track of all fields (major/minor/inst/name) */ + pmdaCacheStore(diskindom, PMDA_CACHE_ADD, namebuf, dev); + pmdaCacheLookupName(diskindom, namebuf, &dev->inst, NULL); + pmdaCacheLookup(diskindom, dev->inst, &dev->name, NULL); + + snprintf(buf, sizeof(buf), "%u:%u", major, minor); + pmdaCacheStore(devtindom, PMDA_CACHE_ADD, buf, (void *)dev); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "refresh_devices: \"%s\" \"%d:%d\" inst=%d\n", + dev->name, dev->major, dev->minor, dev->inst); + } + fclose(fp); +} + +void +refresh_cgroup_subsys(pmInDom indom) +{ + char buf[4096]; + static time_t before; + time_t now; + FILE *fp; + + if ((now = time(NULL)) == before) + return; + before = now; + + if ((fp = proc_statsfile("/proc/cgroups", buf, sizeof(buf))) == NULL) + return; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + unsigned int numcgroups, enabled; + char name[MAXPATHLEN]; + long hierarchy; + long *data; + int sts; + + /* skip lines starting with hash (header) */ + if (buf[0] == '#') + continue; + if (sscanf(buf, "%s %ld %u %u", &name[0], + &hierarchy, &numcgroups, &enabled) != 4) + continue; + sts = pmdaCacheLookupName(indom, name, NULL, (void **)&data); + if (sts == PMDA_CACHE_ACTIVE) { + if (*data != hierarchy) { + /* + * odd ... instance name repeated but different + * hierarchy ... we cannot support more than one hierarchy + * yet + */ + fprintf(stderr, "refresh_cgroup_subsys: \"%s\": entries for hierarchy %ld ignored (hierarchy %ld seen first)\n", name, hierarchy, *data); + } + continue; + } + else if (sts != PMDA_CACHE_INACTIVE) { + if ((data = (long *)malloc(sizeof(long))) == NULL) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "refresh_cgroup_subsys: \"%s\": malloc failed\n", name); +#endif + continue; + } + *data = hierarchy; + } + pmdaCacheStore(indom, PMDA_CACHE_ADD, name, (void *)data); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "refresh_cgroup_subsys: add \"%s\" [hierarchy %ld]\n", name, hierarchy); +#endif + } + fclose(fp); +} + +void +refresh_cgroup_filesys(pmInDom indom) +{ + char buf[MAXPATHLEN]; + filesys_t *fs; + FILE *fp; + time_t now; + static time_t before; + char *path, *device, *type, *options; + int sts; + + if ((now = time(NULL)) == before) + return; + before = now; + + pmdaCacheOp(indom, PMDA_CACHE_INACTIVE); + + if ((fp = proc_statsfile("/proc/mounts", buf, sizeof(buf))) == NULL) + return; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + device = strtok(buf, " "); + path = strtok(NULL, " "); + type = strtok(NULL, " "); + options = strtok(NULL, " "); + if (strcmp(type, "cgroup") != 0) + continue; + + sts = pmdaCacheLookupName(indom, path, NULL, (void **)&fs); + if (sts == PMDA_CACHE_ACTIVE) /* repeated line in /proc/mounts? */ + continue; + if (sts == PMDA_CACHE_INACTIVE) { /* re-activate an old mount */ + pmdaCacheStore(indom, PMDA_CACHE_ADD, path, fs); + if (strcmp(path, fs->path) != 0) { /* old device, new path */ + free(fs->path); + fs->path = strdup(path); + } + if (strcmp(options, fs->options) != 0) { /* old device, new opts */ + free(fs->options); + fs->options = strdup(options); + } + } + else { /* new mount */ + if ((fs = malloc(sizeof(filesys_t))) == NULL) + continue; + fs->path = strdup(path); + fs->options = strdup(options); + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "refresh_filesys: add \"%s\" \"%s\"\n", + fs->path, device); + pmdaCacheStore(indom, PMDA_CACHE_ADD, path, fs); + } + } + fclose(fp); +} + +static char * +scan_filesys_options(const char *options, const char *option) +{ + static char buffer[128]; + char *s; + + strncpy(buffer, options, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + + s = strtok(buffer, ","); + while (s) { + if (strcmp(s, option) == 0) + return s; + s = strtok(NULL, ","); + } + return NULL; +} + +static int +read_values(char *buffer, int size, const char *path, const char *subsys, + const char *metric) +{ + int fd, count; + + snprintf(buffer, size, "%s/%s.%s", path, subsys, metric); + if ((fd = open(buffer, O_RDONLY)) < 0) + return -oserror(); + count = read(fd, buffer, size); + close(fd); + if (count < 0) + return -oserror(); + buffer[count-1] = '\0'; + return 0; +} + +static pmID +update_pmns(__pmnsTree *pmns, cgroup_subsys_t *subsys, const char *name, + cgroup_metrics_t *metrics, int group, int domain) +{ + char entry[MAXPATHLEN]; + pmID pmid; + + snprintf(entry, sizeof(entry), "%s.%s%s.%s", + CGROUP_ROOT, subsys->name, name, metrics->suffix); + pmid = cgroup_pmid_build(domain, subsys->cluster, group, metrics->item); + __pmAddPMNSNode(pmns, pmid, entry); + return pmid; +} + +static int +prepare_ull(__pmnsTree *pmns, const char *path, cgroup_subsys_t *subsys, + const char *name, int metric, int group, int domain) +{ + int count = 0; + unsigned long long value; + char buffer[MAXPATHLEN]; + char *endp, *p = &buffer[0]; + cgroup_group_t *groups = &subsys->groups[group]; + cgroup_metrics_t *metrics = &subsys->metrics[metric]; + pmAtomValue *atoms = groups->metric_values[metric].atoms; + + if (read_values(p, sizeof(buffer), path, subsys->name, metrics->suffix) < 0) + return -oserror(); + + while (p && *p) { + value = strtoull(p, &endp, 0); + if ((atoms = realloc(atoms, (count + 1) * sizeof(pmAtomValue))) == NULL) + return -oserror(); + atoms[count++].ull = value; + if (endp == '\0' || endp == p) + break; + p = endp; + while (p && isspace((int)*p)) + p++; + } + + groups->metric_values[metric].item = metric; + groups->metric_values[metric].atoms = atoms; + groups->metric_values[metric].atom_count = count; + update_pmns(pmns, subsys, name, metrics, group, domain); + return 0; +} + +static int +prepare_named_ull(__pmnsTree *pmns, const char *path, cgroup_subsys_t *subsys, + const char *name, int metric, int group, int domain) +{ + int i, count; + unsigned long long value; + char filename[64], buffer[MAXPATHLEN]; + char *offset, *p = &buffer[0]; + cgroup_group_t *groups = &subsys->groups[group]; + cgroup_metrics_t *metrics = &subsys->metrics[metric]; + + /* metric => e.g. stat.user and stat.system - split it up first */ + offset = index(metrics->suffix, '.'); + if (!offset) + return PM_ERR_CONV; + count = (offset - metrics->suffix); + strncpy(filename, metrics->suffix, count); + filename[count] = '\0'; + + if (read_values(p, sizeof(buffer), path, subsys->name, filename) < 0) + return -oserror(); + + /* buffer contains pairs */ + while (p && *p) { + char *endp, *field, *offset; + + if ((field = index(p, ' ')) == NULL) + return PM_ERR_CONV; + offset = field + 1; + *field = '\0'; + field = p; /* field now points to */ + p = offset; + value = strtoull(p, &endp, 0); + p = endp; + while (p && isspace((int)*p)) + p++; + + for (i = 0; i < subsys->metric_count; i++) { + pmAtomValue *atoms = groups->metric_values[i].atoms; + metrics = &subsys->metrics[i]; + + if (strcmp(field, metrics->suffix + count + 1) != 0) + continue; + if ((atoms = groups->metric_values[i].atoms) == NULL) + if ((atoms = calloc(1, sizeof(pmAtomValue))) == NULL) + return -oserror(); + atoms[0].ull = value; + + groups->metric_values[i].item = i; + groups->metric_values[i].atoms = atoms; + groups->metric_values[i].atom_count = 1; + update_pmns(pmns, subsys, name, metrics, group, domain); + break; + } + } + return 0; +} + +static int +prepare_block(__pmnsTree *pmns, const char *path, cgroup_subsys_t *subsys, + const char *name, int metric, int group, int domain, + block_stats_t *stats, int value_count) +{ + pmID pmid; + char *iname; + char buf[MAXPATHLEN]; + device_t *dev; + pmAtomValue *atoms; + int count, size, inst, sts, m, i, j; + pmInDom devtindom = INDOM(DEVT_INDOM); + cgroup_group_t *groups = &subsys->groups[group]; + cgroup_metrics_t *metrics = &subsys->metrics[metric]; + + /* map major:minor to real device name via diskstats */ + dev = &stats->dev; + snprintf(buf, sizeof(buf), "%u:%u", dev->major, dev->minor); + + sts = pmdaCacheLookupName(devtindom, buf, NULL, (void **)&dev); + iname = dev->name; + inst = dev->inst; + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "prepare_block: preparing %s found=%s (%s)\n", + buf, sts == PMDA_CACHE_ACTIVE ? "ok" : "no", iname); + + /* batch update metric value(s) now, since we have 'em all */ + for (j = 0; j < value_count; j++) { + m = metric + j; + atoms = groups->metric_values[m].atoms; + count = groups->metric_values[m].atom_count; + + if (inst >= count) { + size = (inst + 1) * sizeof(pmAtomValue); + if ((atoms = realloc(atoms, size)) == NULL) + return -oserror(); + for (i = count; i < inst + 1; i++) + atoms[i].ull = ULLONG_MAX; + count = inst + 1; + } + /* move on-stack value into global struct, add to PMNS */ + atoms[inst].ull = stats->values[j]; + pmid = update_pmns(pmns, subsys, name, metrics + j, group, domain); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "prepare_block: prepared " + "metric=%s inst=%s[%d] value=%llu\n", + pmIDStr(pmid), iname, inst, + (unsigned long long)atoms[inst].ull); + + groups->metric_values[m].item = m; + groups->metric_values[m].atoms = atoms; + groups->metric_values[m].atom_count = count; + } + return 0; +} + +static int +prepare_block_ull(__pmnsTree *pmns, const char *path, cgroup_subsys_t *subsys, + const char *name, int metric, int group, int domain) +{ + char buf[MAXPATHLEN]; + cgroup_metrics_t *metrics = &subsys->metrics[metric]; + block_stats_t stats; + FILE *fp; + char *p; + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "prepare_block_ull: %s metric=%d group=%d domain=%d\n", + path, metric, group, domain); + + snprintf(buf, sizeof(buf), "%s/%s.%s", path, subsys->name, metrics->suffix); + if ((fp = fopen(buf, "r")) == NULL) + return -oserror(); + + memset(&stats, 0, sizeof(stats)); + while ((fgets(buf, sizeof(buf), fp)) != NULL) { + if (sscanf(buf, "%u:%u ", &stats.dev.major, &stats.dev.minor) != 2) + continue; + for (p = buf; *p && !isspace(*p); p++) { } /* skip device number */ + for (p = buf; *p && isspace(*p); p++) { } /* skip over spaces */ + if (sscanf(p, "%llu", (unsigned long long *)&stats.values[0]) != 1) + stats.values[0] = 0; + prepare_block(pmns, path, subsys, name, + metric, group, domain, &stats, 1); + } + fclose(fp); + return 0; +} + +static int +prepare_blocks_ull(__pmnsTree *pmns, const char *path, cgroup_subsys_t *subsys, + const char *name, int metric, int group, int domain) +{ + char buf[MAXPATHLEN]; + cgroup_metrics_t *metrics = &subsys->metrics[metric]; + block_stats_t stats; + FILE *fp; + char *p; + int j; + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "prepare_blocks_ull: %s metric=%d group=%d domain=%d\n", + path, metric, group, domain); + + if (metric % BLKIOS != 0) + return 0; + + snprintf(buf, sizeof(buf), "%s/%s.%s", path, subsys->name, metrics->suffix); + buf[strlen(buf) - sizeof("read")] = '\0'; + + if (pmDebug & DBG_TRACE_APPL2) + fprintf(stderr, "prepare_blocks_ull: opening \"%s\"\n", buf); + + if ((fp = fopen(buf, "r")) == NULL) + return -oserror(); + + memset(&stats, 0, sizeof(stats)); + while ((fgets(buf, sizeof(buf), fp)) != NULL) { + if (sscanf(buf, "%u:%u ", &stats.dev.major, &stats.dev.minor) != 2) + continue; + + /* iterate over read/write/sync/async/total (reverse for async) */ + for (j = BLKIOS-1; j >= 0; j--) { + if ((p = strcasestr(buf, block_stats_names[j])) == NULL) + continue; + p += strlen(block_stats_names[j]) + 1; + if (sscanf(p, "%llu", (unsigned long long *)&stats.values[j]) != 1) + stats.values[j] = 0; + break; + } + + if (j == BLKIOS - 1) { /* Total: last one, update incore structures */ + prepare_block(pmns, path, subsys, name, + metric, group, domain, &stats, BLKIOS); + /* reset on-stack structure for next outer loop iteration */ + memset(&stats, 0, sizeof(stats)); + } + } + fclose(fp); + return 0; +} + +static int +prepare_string(__pmnsTree *pmns, const char *path, cgroup_subsys_t *subsys, + const char *name, int metric, int group, int domain) +{ + char buffer[MAXPATHLEN]; + cgroup_group_t *groups = &subsys->groups[group]; + cgroup_metrics_t *metrics = &subsys->metrics[metric]; + pmAtomValue *atoms = groups->metric_values[metric].atoms; + char *p = &buffer[0]; + + if (read_values(p, sizeof(buffer), path, subsys->name, metrics->suffix) < 0) + return -oserror(); + + if ((atoms = malloc(sizeof(pmAtomValue))) == NULL) + return -oserror(); + if ((atoms[0].cp = strdup(buffer)) == NULL) { + free(atoms); + return -oserror(); + } + groups->metric_values[metric].item = metric; + groups->metric_values[metric].atoms = atoms; + groups->metric_values[metric].atom_count = 1; + update_pmns(pmns, subsys, name, metrics, group, domain); + return 0; +} + +static void +translate(char *dest, const char *src, size_t size) +{ + char *p; + + if (*src != '\0') /* non-root */ + *dest = '.'; + strncpy(dest, src, size); + for (p = dest; *p; p++) { + if (*p == '/') + *p = '.'; + } +} + +static int +namespace(__pmnsTree *pmns, cgroup_subsys_t *subsys, + const char *cgrouppath, const char *cgroupname, int domain) +{ + int i, id; + size_t size; + cgroup_values_t *cvp; + char group[128]; + + translate(&group[0], cgroupname, sizeof(group)); + + /* allocate space for this group */ + size = (subsys->group_count + 1) * sizeof(cgroup_group_t); + subsys->groups = (cgroup_group_t *)realloc(subsys->groups, size); + if (subsys->groups == NULL) + return -oserror(); + + /* allocate space for all values up-front */ + size = subsys->metric_count; + cvp = (cgroup_values_t *)calloc(size, sizeof(cgroup_values_t)); + if (cvp == NULL) + return -oserror(); + + id = subsys->group_count++; + memset(&subsys->groups[id], 0, sizeof(cgroup_group_t)); + subsys->groups[id].id = id; + subsys->groups[id].metric_values = cvp; + + for (i = 0; i < size; i++) { + cgroup_metrics_t *metrics = &subsys->metrics[i]; + metrics->prepare(pmns, cgrouppath, subsys, group, i, id, domain); + } + return 1; +} + +char * +cgroup_find_subsys(pmInDom indom, void *data) +{ + static char dunno[] = "?"; + static char opts[256]; + char buffer[256]; + char *s, *out = NULL; + filesys_t *fs = (filesys_t *)data; + + memset(opts, 0, sizeof(opts)); + strncpy(buffer, fs->options, sizeof(buffer)); + + s = strtok(buffer, ","); + while (s) { + if (pmdaCacheLookupName(indom, s, NULL, NULL) == PMDA_CACHE_ACTIVE) { + if (out) { /* append option */ + strcat(out, ","); + strcat(out, s); + out += strlen(s) + 1; /* +1 => cater for comma */ + } else { /* first option */ + strcat(opts, s); + out = opts + strlen(s); + } + } + s = strtok(NULL, ","); + } + if (out) + return opts; + return dunno; +} + +/* Ensure cgroup name can be used as a PCP namespace entry, ignore it if not */ +static int +valid_pmns_name(char *name) +{ + if (!isalpha((int)name[0])) + return 0; + for (; *name != '\0'; name++) + if (!isalnum((int)*name) && *name != '_') + return 0; + return 1; +} + +static int +cgroup_scan(const char *mnt, const char *path, cgroup_subsys_t *subsys, + int domain, __pmnsTree *pmns, int root) +{ + int sts, length; + DIR *dirp; + struct stat sbuf; + struct dirent *dp; + char *cgroupname; + char cgrouppath[MAXPATHLEN]; + + if (root) { + snprintf(cgrouppath, sizeof(cgrouppath), "%s%s", proc_statspath, mnt); + length = strlen(cgrouppath); + } else { + snprintf(cgrouppath, sizeof(cgrouppath), "%s%s/%s", proc_statspath, mnt, path); + length = strlen(proc_statspath) + strlen(mnt) + 1; + } + + if ((dirp = opendir(cgrouppath)) == NULL) + return -oserror(); + + cgroupname = &cgrouppath[length]; + sts = namespace(pmns, subsys, cgrouppath, cgroupname, domain); + + /* + * readdir - descend into directories to find all cgroups, then + * populate namespace with [.]. + */ + while ((dp = readdir(dirp)) != NULL) { + int lsts; + if (!valid_pmns_name(dp->d_name)) + continue; + if (path[0] == '\0') + snprintf(cgrouppath, sizeof(cgrouppath), "%s%s/%s", + proc_statspath, mnt, dp->d_name); + else + snprintf(cgrouppath, sizeof(cgrouppath), "%s%s/%s/%s", + proc_statspath, mnt, path, dp->d_name); + cgroupname = &cgrouppath[length]; + if (stat(cgrouppath, &sbuf) < 0) + continue; + if (!(S_ISDIR(sbuf.st_mode))) + continue; + + lsts = namespace(pmns, subsys, cgrouppath, cgroupname, domain); + if (lsts > 0) + sts = 1; + + /* + * also scan for any child cgroups, but cgroup_scan() may return + * an error + */ + lsts = cgroup_scan(mnt, cgroupname, subsys, domain, pmns, 0); + if (lsts > 0) + sts = 1; + } + closedir(dirp); + return sts; +} + +static void +reset_subsys_stats(cgroup_subsys_t *subsys) +{ + int g, k, a; + + for (g = 0; g < subsys->group_count; g++) { + cgroup_group_t *group = &subsys->groups[g]; + for (k = 0; k < subsys->metric_count; k++) { + pmAtomValue *atoms = group->metric_values[k].atoms; + if (subsys->metrics[k].dynamic) + for (a = 0; a < group->metric_values[k].atom_count; a++) + free(atoms[a].cp); + free(atoms); + } + free(group->metric_values); + if (group->process_list.size) + free(group->process_list.pids); + memset(group, 0, sizeof(cgroup_group_t)); + } + subsys->group_count = 0; +} + +int +refresh_cgroups(pmdaExt *pmda, __pmnsTree **pmns) +{ + int i, sts, mtab = 0; + int domain = pmda->e_domain; + filesys_t *fs; + time_t now; + static time_t before; + static __pmnsTree *beforetree; + __pmnsTree *tree = pmns ? *pmns : NULL; + pmInDom mounts = INDOM(CGROUP_MOUNTS_INDOM); + pmInDom devices = INDOM(DISK_INDOM); + + now = time(NULL); + if (tree) { + if (now == before) { + *pmns = beforetree; + return 0; + } + } else if (now == before) + return 0; + + refresh_cgroup_filesys(mounts); + refresh_cgroup_devices(devices); + + if (tree) + __pmFreePMNS(tree); + + if ((sts = __pmNewPMNS(&tree)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create new pmns: %s\n", + pmProgname, pmErrStr(sts)); + return 0; + } + + for (i = 0; i < sizeof(controllers)/sizeof(controllers[0]); i++) { + cgroup_subsys_t *subsys = &controllers[i]; + + /* + * Fetch latest state for subsystem and groups of the given clusters, + * by walking the cgroup mounts, finding the mounts of this subsystem + * type, and descending into all of the groups (subdirs) + */ + reset_subsys_stats(subsys); + + pmdaCacheOp(mounts, PMDA_CACHE_WALK_REWIND); + while ((sts = pmdaCacheOp(mounts, PMDA_CACHE_WALK_NEXT)) != -1) { + if (!pmdaCacheLookup(mounts, sts, NULL, (void **)&fs)) + continue; + if (scan_filesys_options(fs->options, subsys->name) == NULL) + continue; + sts = cgroup_scan(fs->path, "", subsys, domain, tree, 1); + if (sts > 0) + mtab = 1; + } + } + + if (pmns) { + *pmns = tree; + beforetree = tree; + before = now; + } else + __pmFreePMNS(tree); + + return mtab; +} + +/* + * Shared fetch callback for all cgroups metrics + */ +int +cgroup_group_fetch(pmID pmid, unsigned int inst, pmAtomValue *atom) +{ + int i, j, k; + int gid, cluster, metric; + + gid = cgroup_pmid_group(pmid); + metric = cgroup_pmid_metric(pmid); + cluster = proc_pmid_cluster(pmid); + + for (i = 0; i < sizeof(controllers)/sizeof(controllers[0]); i++) { + cgroup_subsys_t *subsys = &controllers[i]; + + if (subsys->cluster != cluster) + continue; + for (j = 0; j < subsys->group_count; j++) { + cgroup_group_t *group = &subsys->groups[j]; + + if (group->id != gid) + continue; + for (k = 0; k < subsys->metric_count; k++) { + cgroup_values_t *cvp = &group->metric_values[k]; + + if (cvp->item != metric) + continue; + else if (cvp->atom_count <= 0) + return PM_ERR_VALUE; + else if (inst == PM_IN_NULL) + inst = 0; + else if (inst >= cvp->atom_count) + return PM_ERR_INST; + else if (cvp->atoms[inst].ull == ULLONG_MAX) + return PM_ERR_INST; + *atom = cvp->atoms[inst]; + return 1; + } + } + } + return PM_ERR_PMID; +} + +/* + * Needs to answer the question: how much extra space needs to be allocated + * in the metric table for (dynamic) cgroup metrics"? We have static entries + * for group ID zero - if we have any non-zero group IDs, we need entries to + * cover those. Return value is the number of additional entries needed. + */ +static void +size_metrictable(int *total, int *trees) +{ + int i, g, maxgroup = 0, nmetrics = 0; + + for (i = 0; i < sizeof(controllers)/sizeof(controllers[0]); i++) { + cgroup_subsys_t *subsys = &controllers[i]; + + for (g = 0; g < subsys->group_count; g++) { + cgroup_group_t *group = &subsys->groups[g]; + + if (group->id > maxgroup) + maxgroup = group->id; + } + nmetrics += subsys->metric_count + 0; /* +1 for task.pid */ + } + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "size_metrictable: %d total x %d trees\n", + nmetrics, maxgroup); + + *total = nmetrics; + *trees = maxgroup; +} + +/* + * Create new metric table entry for a group based on an existing one. + */ +static void +refresh_metrictable(pmdaMetric *source, pmdaMetric *dest, int gid) +{ + int domain = pmid_domain(source->m_desc.pmid); + int cluster = proc_pmid_cluster(source->m_desc.pmid); + int item = pmid_item(source->m_desc.pmid); + + memcpy(dest, source, sizeof(pmdaMetric)); + dest->m_desc.pmid = cgroup_pmid_build(domain, cluster, gid, item); + + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "refresh_metrictable: (%p -> %p)\n", source, dest); + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "cgroup metric ID dup: %d.[%d.%d].%d - %d.[%d.%d].%d\n", + domain, cluster, + cgroup_pmid_group(source->m_desc.pmid), + cgroup_pmid_metric(source->m_desc.pmid), + pmid_domain(dest->m_desc.pmid), + proc_pmid_cluster(dest->m_desc.pmid), + cgroup_pmid_group(dest->m_desc.pmid), + cgroup_pmid_metric(dest->m_desc.pmid)); +} + +static int +cgroup_text(pmdaExt *pmda, pmID pmid, int type, char **buf) +{ + return PM_ERR_TEXT; +} + +static void +cgroup_metrics_init(pmdaMetric *metrics, int nmetrics) +{ + int i, j, item, cluster = 0; + + for (i = 0; i < sizeof(controllers)/sizeof(controllers[0]); i++) { + cgroup_subsys_t *subsys = &controllers[i]; + + /* set initial default values for controller metrics item field */ + for (j = 0; j < subsys->metric_count; j++) + subsys->metrics[j].item = j; + + /* set initial seed values for dynamic PMIDs in global metric table */ + for (j = item = 0; j < nmetrics; j++) { + if (pmid_cluster(metrics[j].m_desc.pmid) == subsys->cluster) { + if (cluster != subsys->cluster) { + cluster = subsys->cluster; + item = 0; + } + metrics[j].m_desc.pmid = PMDA_PMID(cluster, item++); + } + } + } +} + +void +cgroup_init(pmdaMetric *metrics, int nmetrics) +{ + static int set[] = { + CLUSTER_BLKIO_GROUPS, + CLUSTER_CPUSET_GROUPS, + CLUSTER_CPUACCT_GROUPS, + CLUSTER_CPUSCHED_GROUPS, + CLUSTER_MEMORY_GROUPS, + CLUSTER_NET_CLS_GROUPS, + }; + + cgroup_metrics_init(metrics, nmetrics); + + pmdaDynamicPMNS(CGROUP_ROOT, + set, sizeof(set) / sizeof(set[0]), + refresh_cgroups, cgroup_text, + refresh_metrictable, size_metrictable, + metrics, nmetrics); + pmdaDynamicSetClusterMask(CGROUP_ROOT, CGROUP_MASK); +} diff --git a/src/pmdas/linux_proc/cgroups.h b/src/pmdas/linux_proc/cgroups.h new file mode 100644 index 0000000..d2ec430 --- /dev/null +++ b/src/pmdas/linux_proc/cgroups.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _CGROUP_H +#define _CGROUP_H + +/* + * Note: cgroup metrics have an "extra" component - the cluster part + * of the PMID (12 bits) is split into two (6 bits each): the bottom + * part contains the regular metric (cluster) ID while the top holds + * the cgroup ID (index - e.g. this is the 3rd cgroup we've seen for + * a particular subsystem). + */ + +#define CGROUP_SPLIT 6 +#define CGROUP_MASK ((1 << CGROUP_SPLIT) - 1) + +static inline pmID +cgroup_pmid_build(unsigned int domain, unsigned int cluster, + unsigned int gid, unsigned int metric) +{ + return pmid_build(domain, (gid << CGROUP_SPLIT) | cluster, metric); +} + +static inline unsigned int +cgroup_pmid_group(pmID id) +{ + return pmid_cluster(id) >> CGROUP_SPLIT; +} + +static inline unsigned int +proc_pmid_cluster(pmID id) +{ + return pmid_cluster(id) & CGROUP_MASK; +} + +static inline unsigned int +cgroup_pmid_metric(pmID id) +{ + return pmid_item(id); +} + +/* + * General cgroup interfaces + */ +extern void cgroup_init(pmdaMetric *, int); +extern char *cgroup_find_subsys(pmInDom, void *); +extern int cgroup_group_fetch(pmID, unsigned int, pmAtomValue *); + +/* + * Metric name and value refresh interfaces + */ +extern int refresh_cgroups(pmdaExt *, __pmnsTree **); + +/* + * Indom-specific interfaces + */ +extern void refresh_cgroup_cpus(pmInDom); +extern void refresh_cgroup_devices(pmInDom); +extern void refresh_cgroup_filesys(pmInDom); +extern void refresh_cgroup_subsys(pmInDom); + +#endif /* _CGROUP_H */ diff --git a/src/pmdas/linux_proc/clusters.h b/src/pmdas/linux_proc/clusters.h new file mode 100644 index 0000000..e1c8c2a --- /dev/null +++ b/src/pmdas/linux_proc/clusters.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 2005,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _CLUSTERS_H +#define _CLUSTERS_H + +/* + * fetch cluster numbers ... to manage the PMID migration after the + * linux -> linux + proc PMDAs split, these need to match the enum + * assigned values for CLUSTER_* from the linux PMDA. + */ +#define CLUSTER_PID_STAT 8 /* /proc//stat */ +#define CLUSTER_PID_STATM 9 /* /proc//statm + /proc//maps */ +#define CLUSTER_CONTROL 10 /* instance + value fetch control metrics */ +#define CLUSTER_PID_CGROUP 11 /* /proc//cgroup */ +#define CLUSTER_PID_LABEL 12 /* /proc//attr/current (label) */ +#define CLUSTER_PROC_RUNQ 13 /* number of processes in various states */ +#define CLUSTER_PID_STATUS 24 /* /proc//status */ +#define CLUSTER_PID_SCHEDSTAT 31 /* /proc//schedstat */ +#define CLUSTER_PID_IO 32 /* /proc//io */ +#define CLUSTER_CGROUP_SUBSYS 37 /* /proc/cgroups control group subsystems */ +#define CLUSTER_CGROUP_MOUNTS 38 /* /proc/mounts active control groups */ +#define CLUSTER_CPUSET_GROUPS 39 /* cpuset control groups */ +#define CLUSTER_CPUACCT_GROUPS 41 /* cpu accounting control groups */ +#define CLUSTER_CPUSCHED_GROUPS 43 /* scheduler control groups */ +#define CLUSTER_MEMORY_GROUPS 45 /* memory control groups */ +#define CLUSTER_NET_CLS_GROUPS 47 /* network classification control groups */ +#define CLUSTER_BLKIO_GROUPS 49 /* blkio control groups */ +#define CLUSTER_PID_FD 51 /* /proc//fd */ + /* Note: do not use higher than (1 << CGROUP_SPLIT)-1 as cluster ID */ + +#define MIN_CLUSTER 8 /* first cluster number we use here */ +#define NUM_CLUSTERS 52 /* one more than highest cluster number used */ +#define MAX_CLUSTER 63 /* last available - fill gaps if more needed */ + +#endif /* _CLUSTERS_H */ diff --git a/src/pmdas/linux_proc/contexts.c b/src/pmdas/linux_proc/contexts.c new file mode 100644 index 0000000..f213c14 --- /dev/null +++ b/src/pmdas/linux_proc/contexts.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "contexts.h" + +static proc_perctx_t *ctxtab; +static int num_ctx; +static uid_t baseuid; +static gid_t basegid; + +static void +proc_ctx_clear(int ctx) +{ + ctxtab[ctx].state = CTX_INACTIVE; + ctxtab[ctx].uid = -1; + ctxtab[ctx].gid = -1; + ctxtab[ctx].threads = 1; + ctxtab[ctx].cgroups = NULL; +} + +void +proc_ctx_end(int ctx) +{ + if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) + return; + if (ctxtab[ctx].state & CTX_CGROUPS) + free((void *)ctxtab[ctx].cgroups); + proc_ctx_clear(ctx); +} + +static void +proc_ctx_growtab(int ctx) +{ + size_t need; + + if (ctx < num_ctx) + return; + + need = (ctx + 1) * sizeof(ctxtab[0]); + ctxtab = (proc_perctx_t *)realloc(ctxtab, need); + if (ctxtab == NULL) + __pmNoMem("proc ctx table", need, PM_FATAL_ERR); + while (num_ctx <= ctx) + proc_ctx_clear(num_ctx++); +} + +static void +proc_ctx_set_userid(int ctx, const char *value) +{ + proc_ctx_growtab(ctx); + ctxtab[ctx].uid = atoi(value); + ctxtab[ctx].state |= (CTX_ACTIVE | CTX_USERID); +} + +static void +proc_ctx_set_groupid(int ctx, const char *value) +{ + proc_ctx_growtab(ctx); + ctxtab[ctx].gid = atoi(value); + ctxtab[ctx].state |= (CTX_ACTIVE | CTX_GROUPID); +} + +int +proc_ctx_attrs(int ctx, int attr, const char *value, int length, pmdaExt *pmda) +{ + if (pmDebug & DBG_TRACE_AUTH) { + char buffer[256]; + + if (!__pmAttrStr_r(attr, value, buffer, sizeof(buffer))) { + __pmNotifyErr(LOG_ERR, "Bad Attribute: ctx=%d, attr=%d\n", ctx, attr); + } else { + buffer[sizeof(buffer)-1] = '\0'; + __pmNotifyErr(LOG_INFO, "Attribute: ctx=%d %s", ctx, buffer); + } + } + + switch (attr) { + case PCP_ATTR_USERID: + proc_ctx_set_userid(ctx, value); + break; + case PCP_ATTR_GROUPID: + proc_ctx_set_groupid(ctx, value); + break; + default: + break; + } + return 0; +} + +void +proc_ctx_init(void) +{ + baseuid = getuid(); + basegid = getgid(); +} + +int +proc_ctx_access(int ctx) +{ + proc_perctx_t *pp; + int accessible = 0; + + if (ctx < 0 || ctx >= num_ctx) + return accessible; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return accessible; + + if (pp->state & CTX_GROUPID) { + accessible++; + if (basegid != pp->gid) { + if (setegid(pp->gid) < 0) { + __pmNotifyErr(LOG_ERR, "setegid(%d) access failed: %s\n", + pp->gid, osstrerror()); + accessible--; + } + } + } + if (pp->state & CTX_USERID) { + accessible++; + if (baseuid != pp->uid) { + if (seteuid(pp->uid) < 0) { + __pmNotifyErr(LOG_ERR, "seteuid(%d) access failed: %s\n", + pp->uid, osstrerror()); + accessible--; + } + } + } + return (accessible > 1); +} + +int +proc_ctx_revert(int ctx) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return 0; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return 0; + + if ((pp->state & CTX_USERID) && baseuid != pp->uid) { + if (seteuid(baseuid) < 0) + __pmNotifyErr(LOG_ERR, "seteuid(%d) revert failed: %s\n", + baseuid, osstrerror()); + } + if ((pp->state & CTX_GROUPID) && basegid != pp->gid) { + if (setegid(basegid) < 0) + __pmNotifyErr(LOG_ERR, "setegid(%d) revert failed: %s\n", + basegid, osstrerror()); + } + return 0; +} + +unsigned int +proc_ctx_threads(int ctx, unsigned int threads) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return threads; /* fallback to default */ + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return threads; /* fallback to default */ + + if (pp->state & CTX_THREADS) + return pp->threads; /* client setting */ + + return threads; /* fallback to default */ +} + +int +proc_ctx_set_threads(int ctx, unsigned int threads) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return PM_ERR_NOCONTEXT; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return PM_ERR_NOCONTEXT; + if (threads > 1) + return PM_ERR_CONV; + + pp->state |= CTX_THREADS; + pp->threads = threads; + return 0; +} + +const char * +proc_ctx_cgroups(int ctx, const char *cgroups) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return cgroups; /* fallback to default */ + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return cgroups; /* fallback to default */ + + if (pp->state & CTX_CGROUPS) + return pp->cgroups; /* client setting */ + + return cgroups; /* fallback to default */ +} + +int +proc_ctx_set_cgroups(int ctx, const char *cgroups) +{ + proc_perctx_t *pp; + + if (ctx < 0 || ctx >= num_ctx) + return PM_ERR_NOCONTEXT; + pp = &ctxtab[ctx]; + if (pp->state == CTX_INACTIVE) + return PM_ERR_NOCONTEXT; + if (cgroups == NULL || cgroups[0] == '\0') + return PM_ERR_CONV; + + pp->state |= CTX_CGROUPS; + pp->cgroups = cgroups; + return 0; +} diff --git a/src/pmdas/linux_proc/contexts.h b/src/pmdas/linux_proc/contexts.h new file mode 100644 index 0000000..c2abe8c --- /dev/null +++ b/src/pmdas/linux_proc/contexts.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _CONTEXTS_H +#define _CONTEXTS_H + +/* + * Handle newly arriving clients, security attributes being set on 'em, + * switching to alternative accounts (temporarily) and back, and client + * termination. State maintained in a global table, with a high-water + * allocator and active/inactive entry tracking. + * + * The proc.control.perclient metrics also have state tracked here now. + */ + +enum { + CTX_INACTIVE = 0x0, + CTX_ACTIVE = 0x1, + CTX_USERID = 0x2, + CTX_GROUPID = 0x4, + CTX_THREADS = 0x8, + CTX_CGROUPS = 0x10, +}; + +typedef struct { + unsigned int state; + uid_t uid; + gid_t gid; + unsigned int threads; + const char *cgroups; +} proc_perctx_t; + +extern void proc_ctx_init(void); +extern int proc_ctx_attrs(int, int, const char *, int, pmdaExt *); +extern void proc_ctx_end(int); + +extern int proc_ctx_access(int); +extern int proc_ctx_revert(int); + +extern unsigned int proc_ctx_threads(int, unsigned int); +extern int proc_ctx_set_threads(int, unsigned int); + +extern const char *proc_ctx_cgroups(int, const char *); +extern int proc_ctx_set_cgroups(int, const char *); + +#endif /* _CONTEXTS_H */ diff --git a/src/pmdas/linux_proc/getinfo.c b/src/pmdas/linux_proc/getinfo.c new file mode 100644 index 0000000..b4633a5 --- /dev/null +++ b/src/pmdas/linux_proc/getinfo.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010 Aconex. All Rights Reserved. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include "pmapi.h" + +char * +get_ttyname_info(int pid, dev_t dev, char *ttyname) +{ + DIR *dir; + struct dirent *dp; + struct stat sbuf; + int found=0; + char procpath[MAXPATHLEN]; + char ttypath[MAXPATHLEN]; + + sprintf(procpath, "/proc/%d/fd", pid); + if ((dir = opendir(procpath)) != NULL) { + while ((dp = readdir(dir)) != NULL) { + if (!isdigit((int)dp->d_name[0])) + continue; + sprintf(procpath, "/proc/%d/fd/%s", pid, dp->d_name); + if (realpath(procpath, ttypath) == NULL || stat(ttypath, &sbuf) < 0) + continue; + if (S_ISCHR(sbuf.st_mode) && dev == sbuf.st_rdev) { + found=1; + break; + } + } + closedir(dir); + } + + if (!found) + strcpy(ttyname, "?"); + else + /* skip the "/dev/" prefix */ + strcpy(ttyname, &ttypath[5]); + + return ttyname; +} diff --git a/src/pmdas/linux_proc/getinfo.h b/src/pmdas/linux_proc/getinfo.h new file mode 100644 index 0000000..9006c00 --- /dev/null +++ b/src/pmdas/linux_proc/getinfo.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2010 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +extern char *get_ttyname_info(int, dev_t, char *); + diff --git a/src/pmdas/linux_proc/help b/src/pmdas/linux_proc/help new file mode 100644 index 0000000..6640a08 --- /dev/null +++ b/src/pmdas/linux_proc/help @@ -0,0 +1,220 @@ +# +# Copyright (c) 2000,2004-2008 Silicon Graphics, Inc. All Rights Reserved. +# Portions Copyright (c) International Business Machines Corp., 2002 +# Portions Copyright (c) 2007-2009 Aconex. All Rights Reserved. +# Portions Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Linux proc PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ cgroup.subsys.hierarchy subsystem hierarchy from /proc/cgroups +@ cgroup.subsys.count count of known subsystems in /proc/cgroups +@ cgroup.mounts.subsys mount points for each cgroup subsystem +@ cgroup.mounts.count count of cgroup filesystem mount points + +@ proc.nprocs instantaneous number of processes +@ proc.psinfo.pid process identifier +@ proc.psinfo.psargs full command string +@ proc.psinfo.cmd command name +@ proc.psinfo.sname process state identifier (see ps(1)). See also proc.runq metrics. +@ proc.psinfo.ppid parent process identifier +@ proc.psinfo.pgrp process group identifier +@ proc.psinfo.session process session identifier +@ proc.psinfo.tty controlling tty device number (zero if none) +@ proc.psinfo.tty_pgrp controlling tty process group identifier +@ proc.psinfo.flags process state flags, as a bitmap +@ proc.psinfo.minflt count of minor page faults (i.e. reclaims) +@ proc.psinfo.cmin_flt count of minor page faults (i.e. reclaims) of all exited children +@ proc.psinfo.maj_flt count of page faults other than reclaims +@ proc.psinfo.cmaj_flt count of page faults other than reclaims of all exited children +@ proc.psinfo.utime time (in ms) spent executing user code since process started +@ proc.psinfo.stime time (in ms) spent executing system code (calls) since process started +@ proc.psinfo.cutime time (in ms) spent executing user code of all exited children +@ proc.psinfo.cstime time (in ms) spent executing system code of all exited children +@ proc.psinfo.priority priority value +@ proc.psinfo.nice process nice value (negative nice values are lower priority) +@ proc.psinfo.it_real_value current interval timer value (zero if none) +@ proc.psinfo.start_time start time of the process relative to system boot time in seconds +@ proc.psinfo.vsize virtual size of the process in Kbytes +@ proc.psinfo.rss resident set size (i.e. physical memory) of the process +@ proc.psinfo.rss_rlim limit on resident set size of process +@ proc.psinfo.start_code address of the start of the code segment for the process +@ proc.psinfo.end_code address of the end of the code segment for the process +@ proc.psinfo.start_stack address of the stack segment for the process +@ proc.psinfo.esp the value in the esp field of struct task_struct for the process +@ proc.psinfo.eip the value in the eip field of struct task_struct for the process +@ proc.psinfo.signal the value in the signal field of struct task_struct for the process +@ proc.psinfo.blocked the value in the blocked field of struct task_struct for the process +@ proc.psinfo.sigignore the value in the sigignore field of struct task_struct for the process +@ proc.psinfo.sigcatch the value in the sigcatch field of struct task_struct for the process +@ proc.psinfo.wchan wait channel, kernel address this process is blocked or sleeping on +@ proc.psinfo.nswap count of page swap operations +@ proc.psinfo.cnswap count of page swap operations of all exited children +@ proc.psinfo.exit_signal the value in the exit_signal field of struct task_struct for the process +@ proc.psinfo.ttyname name of controlling tty device, or "?" if none. See also proc.psinfo.tty. +@ proc.psinfo.processor last CPU the process was running on +@ proc.psinfo.wchan_s name of an event for which the process is sleeping (if blank, the process is running). +This field needs access to a namelist file for proper +address-to-symbol name translation. If no namelist file +is available, the address is printed instead. The namelist +file must match the current Linux kernel exactly. +The search path for the namelist file is as follows: + /boot/System.map-`uname -r` + /boot/System.map + /lib/modules/`uname -r`/System.map + /usr/src/linux/System.map + /System.map +@ proc.psinfo.signal_s pending signals mask in string form (from /proc//status) +@ proc.psinfo.blocked_s blocked signals mask in string form (from /proc//status) +@ proc.psinfo.sigignore_s ignored signals mask in string form (from /proc//status) +@ proc.psinfo.sigcatch_s caught signals mask in string form (from /proc//status) +@ proc.psinfo.threads number of threads (from /proc//status) +@ proc.psinfo.cgroups list of processes cgroups (from /proc//cgroup) +@ proc.psinfo.labels list of processes security labels (from /proc//attr/current) +@ proc.memory.size instantaneous virtual size of process, excluding page table and task structure. +@ proc.memory.rss instantaneous resident size of process, excluding page table and task structure. +@ proc.memory.share instantaneous amount of memory shared by this process with other processes +@ proc.memory.textrss instantaneous resident size of process code segment in Kbytes +@ proc.memory.librss instantaneous resident size of library code mapped by the process, in Kbytes +@ proc.memory.datrss instantaneous resident size of process data segment, in Kbytes +@ proc.memory.dirty instantaneous amount of memory that has been modified by the process, in Kbytes +@ proc.memory.maps table of memory mapped by process in string form from /proc//maps +@ proc.memory.vmsize total virtual memory (from /proc//status) +@ proc.memory.vmlock locked virtual memory (from /proc//status) +@ proc.memory.vmrss resident virtual memory (from /proc//status) +@ proc.memory.vmdata virtual memory used for data (from /proc//status) +@ proc.memory.vmstack virtual memory used for stack (from /proc//status) +@ proc.memory.vmexe virtual memory used for non-library executable code (from /proc//status) +@ proc.memory.vmlib virtual memory used for libraries (from /proc//status) +@ proc.memory.vmswap virtual memory that has been brought in and out. +@ proc.id.uid real user ID from /proc//status +@ proc.id.euid effective user ID from /proc//status +@ proc.id.suid saved user ID from /proc//status +@ proc.id.fsuid filesystem user ID from /proc//status +@ proc.id.gid real group ID from /proc//status +@ proc.id.egid effective group ID from /proc//status +@ proc.id.sgid saved group ID from /proc//status +@ proc.id.fsgid filesystem group ID from /proc//status +@ proc.id.uid_nm real user name based on real user ID from /proc//status +@ proc.id.euid_nm effective user name based on effective user ID from /proc//status +@ proc.id.suid_nm saved user name based on saved user ID from /proc//status +@ proc.id.fsuid_nm filesystem user name based on filesystem user ID from /proc//status +@ proc.id.gid_nm real group name based on real group ID from /proc//status +@ proc.id.egid_nm effective group name based on effective group ID from /proc//status +@ proc.id.sgid_nm saved group name based on saved group ID from /proc//status +@ proc.id.fsgid_nm filesystem group name based on filesystem group ID from /proc//status + +@ proc.runq.runnable number of runnable (on run queue) processes +Instantaneous number of runnable (on run queue) processes, state 'R' in ps +@ proc.runq.blocked number of processes in uninterruptible sleep +Instantaneous number of processes in uninterruptible sleep, state 'D' in ps +@ proc.runq.sleeping number of processes sleeping +Instantaneous number of processes sleeping, state 'S' in ps +@ proc.runq.stopped number of traced, stopped or suspended processes +Instantaneous number of traced, stopped or suspended processes, state +'T' in ps +@ proc.runq.swapped number of processes that are swapped +Instantaneous number of processes (excluding kernel threads) that are +swapped, state 'SW' in ps +@ proc.runq.defunct number of defunct/zombie processes +Instantaneous number of defunct/zombie processes, state 'Z' in ps +@ proc.runq.unknown number of processes is an unknown state +Instantaneous number of processes is an unknown state, including all +kernel threads +@ proc.runq.kernel number of kernel threads +Instantaneous number of processes with virtual size of zero (kernel threads) + +@ proc.io.rchar read(), readv() and sendfile() receive bytes +Extended accounting information - count of the number of bytes that +have passed over the read(2), readv(2) and sendfile(2) syscalls by +each process. + +@ proc.io.wchar write(), writev() and sendfile() send bytes +Extended accounting information - count of the number of bytes that +have passed over the write(2), writev(2) and sendfile(2) syscalls by +each process. + +@ proc.io.syscr read(), readv() and sendfile() receive system calls +Extended accounting information - count of number of calls to the +read(2), readv(2) and sendfile(2) syscalls by each process. + +@ proc.io.syscw write(), writev() and sendfile() send system calls +Extended accounting information - count of number of calls to the +write(2), writev(2) and sendfile(2) syscalls by each process. + +@ proc.io.read_bytes physical device read bytes +Number of bytes physically read on by devices on behalf of this process. +@ proc.io.write_bytes physical device write bytes +Number of bytes physically written to devices on behalf of this process. +This must be reduced by any truncated I/O (proc.io.cancelled_write_bytes). +@ proc.io.cancelled_write_bytes physical device write cancelled bytes +Number of bytes cancelled via truncate by this process. Actual physical +writes for an individual process can be calculated as: + proc.io.write_bytes - proc.io.cancelled_write_bytes. + +@ proc.schedstat.cpu_time runnable (scheduled) + run time +Length of time in nanoseconds that a process has been running, including +scheduling time. +@ proc.schedstat.run_delay run queue time +Length of time in nanoseconds that a process spent waiting to be scheduled +to run in the run queue. +@ proc.schedstat.pcount number of times a process is allowed to run +Number of times a process has been scheduled to run on a CPU (this is +incremented when a task actually reaches a CPU to run on, not simply +when it is added to the run queue). + +@ proc.fd.count open file descriptors +Number of file descriptors this process has open. + +@ proc.control.all.threads process indom includes threads +If set to one, the process instance domain as reported by pmdaproc +contains all threads as well as the processes that started them. +If set to zero, the process instance domain contains only processes. + +This setting is persistent for the life of pmdaproc and affects all +client tools that request instances and values from pmdaproc. +Use either pmstore(1) or pmStore(3) to modify this metric. + +@ proc.control.perclient.threads for a client, process indom includes threads +If set to one, the process instance domain as reported by pmdaproc +contains all threads as well as the processes that started them. +If set to zero, the process instance domain contains only processes. + +This setting is only visible to the active client context. In other +words, storing into this metric has no effect for other monitoring +tools. See proc.control.all.threads, if that is the desired outcome. +Only pmStore(3) can effectively set this metric (pmstore(1) cannot). + +@ proc.control.perclient.cgroups for a client, process indom reflects specific cgroups +If set to the empty string (the default), the process instance domain +as reported by pmdaproc contains all processes. However, a cgroup +name (full path) can be stored into this metric in order to restrict +processes reported to only those within the specified cgroup. This +set is further affected by the value of proc.control.perclient.threads. + +This setting is only visible to the active client context. In other +words, storing into this metric has no effect for other monitoring +tools. pmStore(3) must be used to set this metric (not pmstore(1)). diff --git a/src/pmdas/linux_proc/indom.h b/src/pmdas/linux_proc/indom.h new file mode 100644 index 0000000..9c928cd --- /dev/null +++ b/src/pmdas/linux_proc/indom.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * Copyright (c) 2005,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _INDOM_H +#define _INDOM_H + +/* + * indom serial numbers ... to manage the indom migration after the + * linux -> linux + proc PMDAs split, these need to match the enum + * assigned values for *_INDOM from the linux PMDA. Consequently, + * the proc indom table is sparse. + */ +#define CPU_INDOM 0 /* - percpu */ +#define DISK_INDOM 1 /* - disks (with normal names) */ +#define DEVT_INDOM 2 /* - disks (major:minor names) */ +#define PROC_INDOM 9 /* - processes */ +#define STRINGS_INDOM 10 /* - fake indom, string hash */ +#define CGROUP_SUBSYS_INDOM 20 /* - control group subsystems */ +#define CGROUP_MOUNTS_INDOM 21 /* - control group mounts */ + +#define MIN_INDOM 0 /* first indom number we use here */ +#define NUM_INDOMS 22 /* one more than highest indom number we use here */ + +extern pmInDom proc_indom(int); +#define INDOM(i) proc_indom(i) + +/* + * Optional path prefix for all stats files, used for testing. + */ +extern char *proc_statspath; +extern FILE *proc_statsfile(const char *, char *, int); + +/* + * static string dictionary - one copy of oft-repeated strings; + * implemented using STRINGS_INDOM and pmdaCache(3) routines. + */ +char *proc_strings_lookup(int); +int proc_strings_insert(const char *); + +#endif /* _INDOM_H */ diff --git a/src/pmdas/linux_proc/ksym.c b/src/pmdas/linux_proc/ksym.c new file mode 100644 index 0000000..1604c84 --- /dev/null +++ b/src/pmdas/linux_proc/ksym.c @@ -0,0 +1,564 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * Copyright (c) 2003,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * This code originally contributed by Mike Mason + * with hints from the procps and ksymoops projects. + */ + +#include +#include +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "ksym.h" +#include "indom.h" + +static struct ksym *ksym_a; +static size_t ksym_a_sz; + +static int +find_index(__psint_t addr, int lo, int hi) +{ + int mid; + + if (lo > hi) { + return -1; + } + + mid = lo + ((hi - lo) / 2); + if (addr == ksym_a[mid].addr || + (addr > ksym_a[mid].addr && addr < ksym_a[mid+1].addr)) { + return mid; + } + + if (addr > ksym_a[mid].addr) + return find_index(addr, mid+1, hi); + else + return find_index(addr, lo, mid-1); +} + +static char * +find_name_by_addr(__psint_t addr) +{ + int ix = -1; + + if (ksym_a) + ix = find_index(addr, 0, ksym_a_sz - 1); + if (ix < 0) + return NULL; + + return ksym_a[ix].name; +} + +static int +find_dup_name(int maxix, __psint_t addr, char *name) +{ + int i, res; + + for (i = 0; i < maxix; i++) { + if (ksym_a[i].name) { + res = strcmp(ksym_a[i].name, name); + if (res > 0) + break; + if (res == 0) { + if (addr == ksym_a[i].addr) + return KSYM_FOUND; + else + return KSYM_FOUND_MISMATCH; + } + } + } + + return KSYM_NOT_FOUND; +} + +/* Brute force linear search to determine if the kernel version + in System.map matches the running kernel version and returns + a tri-state result as follows: + + 0 no match + 1 _end not found but version matched + 2 _end found and matched + */ +static int +validate_sysmap(FILE *fp, char *version, __psint_t end_addr) +{ + __psint_t addr; + char type; + int ret = 0; + char kname[128]; + + while (fscanf(fp, "%p %c %s", (void **)&addr, &type, kname) != EOF) { + if (end_addr && strcmp(kname, "_end") == 0) { + ret = (end_addr == addr) ? 2 : 0; + break; /* no need to look any further */ + } + if (strcmp(kname, version) == 0) + ret = 1; + } + + return ret; +} + +char * +wchan(__psint_t addr) +{ + static char zero; + char *p = NULL; + + if (addr == 0) /* 0 address means not in kernel space */ + p = &zero; + else if ((p = find_name_by_addr(addr))) { + /* strip off "sys_" or leading "_"s if necessary */ + if (strncmp(p, "sys_", 4) == 0) + p += 4; + while (*p == '_' && *p) + ++p; + } + + return p; +} + +static int +ksym_compare_addr(const void *e1, const void *e2) +{ + struct ksym *ks1 = (struct ksym *) e1; + struct ksym *ks2 = (struct ksym *) e2; + + if (ks1->addr < ks2->addr) + return -1; + if (ks1->addr > ks2->addr) + return 1; + return 0; +} + +static int +ksym_compare_name(const void *e1, const void *e2) +{ + struct ksym *ks1 = (struct ksym *) e1; + struct ksym *ks2 = (struct ksym *) e2; + + return(strcmp(ks1->name, ks2->name)); +} + +static int +read_ksyms(__psint_t *end_addr) +{ + char inbuf[256]; + char *ip; + char *sp; + char *tp; + char *p; + int ix = 0; + int l = 0; + int len; + int err; + FILE *fp; + struct ksym *ksym_tmp; + + *end_addr = 0; + if ((fp = proc_statsfile("/proc/ksyms", inbuf, sizeof(inbuf))) == NULL) + return -oserror(); + + while (fgets(inbuf, sizeof(inbuf), fp) != NULL) { + l++; + + /* + * /proc/ksyms lines look like this on ia32 ... + * + * c8804060 __insmod_rtc_S.text_L4576 [rtc] + * c010a320 disable_irq_nosync + * + * else on ia64 ... + * + * a0000000003e0d28 debug [arsess] + * e002100000891140 disable_irq_nosync + */ + + if (strstr(inbuf, "\n") == NULL) { + fprintf(stderr, "read_ksyms: truncated /proc/ksyms line [%d]: %s\n", l-1, inbuf); + continue; + } + + /* Increase array size, if necessary */ + if (ksym_a_sz < ix+1) { + if (ksym_a_sz > 0) + ksym_a_sz += INCR_KSIZE; + else + ksym_a_sz = INIT_KSIZE; + ksym_tmp = (struct ksym *)realloc(ksym_a, ksym_a_sz * sizeof(struct ksym)); + if (ksym_tmp == NULL) { + err = -oserror(); + free(ksym_a); + fclose(fp); + return err; + } + ksym_a = ksym_tmp; + } + + ip = inbuf; + /* parse over address */ + while (isxdigit((int)*ip)) ip++; + + if (!isspace((int)*ip) || ip-inbuf < 4) { + /* bad format line */ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "read_ksyms: bad addr? %c[%d] line=\"%s\"\n", *ip, (int)(ip-inbuf), inbuf); + } +#endif + continue; + } + + sscanf(inbuf, "%p", (void **)&ksym_a[ix].addr); + + while (isblank((int)*ip)) ip++; + + /* next should be the symbol name */ + sp = ip++; + while (!isblank((int)*ip) &&*ip != '\n') ip++; + + /* strip off GPLONLY_ prefix, if found */ + if (strncmp(sp, "GPLONLY_", 8) == 0) + sp += 8; + + /* + * strip off symbol version suffix, if found ... looking for + * trailing pattern of the form _R.*[0-9a-fA-F]{8,} + * - find rightmost _R, if any + */ + tp = sp; + while ((p = strstr(tp, "_R")) != NULL) tp = p+2; + if (tp > sp) { + /* + * found _R, need the last 8 digits to be hex + */ + if (ip - tp + 1 >= 8) { + for (p = &ip[-8]; p < ip; p++) { + if (!isxdigit((int)*p)) { + tp = sp; + break; + } + } + } + else { + /* not enough characters for [0-9a-fA-f]{8,} at the end */ + tp = sp; + } + } + if (tp > sp) + /* need to strip the trailing _R.*[0-9a-fA-f]{8,} */ + len = tp - sp - 2; + else + len = ip - sp + 1; + + ksym_a[ix].name = strndup(sp, len); + if (ksym_a[ix].name == NULL) { + err = -oserror(); + fclose(fp); + return err; + } + ksym_a[ix].name[len-1] = '\0'; + + if (*end_addr == 0 && strcmp(ksym_a[ix].name, "_end") == 0) + *end_addr = ksym_a[ix].addr; + + if (*ip == '\n') + /* nothing after the symbol name, so no module name */ + goto next; + + while (isblank((int)*ip)) ip++; + + /* next expect module name */ + if (*ip != '[') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "read_ksyms: bad start module name %c[%d] != [ line=\"%s\"\n", *ip, (int)(ip-inbuf), inbuf); + } +#endif + free(ksym_a[ix].name); + continue; + } + + sp = ++ip; + while (!isblank((int)*ip) && *ip != ']') ip++; + + if (*ip != ']') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "read_ksyms: bad end module name %c[%d] != ] line=\"%s\"\n", *ip, (int)(ip-inbuf), inbuf); + } +#endif + free(ksym_a[ix].name); + continue; + } + + ksym_a[ix].module = strndup(sp, ip - sp + 1); + if (ksym_a[ix].module == NULL) { + err = -oserror(); + fclose(fp); + free(ksym_a[ix].name); + return err; + } + ksym_a[ix].module[ip - sp] = '\0'; + +next: + ix++; + } + + /* release unused ksym array entries */ + if (ix) { + ksym_tmp = (struct ksym *)realloc(ksym_a, ix * sizeof(struct ksym)); + if (ksym_tmp == NULL) { + free(ksym_a); + fclose(fp); + return -oserror(); + } + ksym_a = ksym_tmp; + } + + ksym_a_sz = ix; + + qsort(ksym_a, ksym_a_sz, sizeof(struct ksym), ksym_compare_name); + + fclose(fp); + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "symbols from ksyms ...\n"); + for (ix = 0; ix < ksym_a_sz; ix++) { + fprintf(stderr, "ksym[%d] " PRINTF_P_PFX "%p %s", ix, (void *)ksym_a[ix].addr, ksym_a[ix].name); + if (ksym_a[ix].module != NULL) fprintf(stderr, " [%s]", ksym_a[ix].module); + fprintf(stderr, "\n"); + } + } +#endif + + return ksym_a_sz; +} + +static int +read_sysmap(const char *release, __psint_t end_addr) +{ + char inbuf[256], path[MAXPATHLEN], **fmt; + struct ksym *ksym_tmp; + __psint_t addr; + int ix, res, e; + int l = 0; + char *ip; + char *sp; + int major, minor, patch; + FILE *fp; + char *bestpath = NULL; + int ksym_mismatch_count; + char *sysmap_paths[] = { /* Paths to check for System.map file */ + "%s/boot/System.map-%s", + "%s/boot/System.map", + "%s/lib/modules/%s/System.map", + "%s/usr/src/linux/System.map", + "%s/System.map", + NULL + }; + + /* Create version symbol name to look for in System.map */ + if (sscanf(release, "%d.%d.%d", &major, &minor, &patch) < 3 ) + return -1; + sprintf(inbuf, "Version_%u", KERNEL_VERSION(major, minor, patch)); + + /* + * Walk through System.map path list looking for one that matches + * either _end from /proc/ksyms or the uts version. + */ + for (fmt = sysmap_paths; *fmt; fmt++) { + snprintf(path, MAXPATHLEN, *fmt, proc_statspath, release); + if ((fp = fopen(path, "r"))) { + if ((e = validate_sysmap(fp, inbuf, end_addr)) != 0) { + if (e == 2) { + /* matched _end, so this is the right System.map */ + if (bestpath) + free(bestpath); + bestpath = strdup(path); + } + else + if (e == 1 && !bestpath) + bestpath = strdup(path); + } + fclose(fp); + if (e == 2) { + /* _end matched => don't look any further */ + break; + } + } + } + + if (bestpath) + fprintf(stderr, "NOTICE: using \"%s\" for kernel symbols map.\n", bestpath); + else { + /* Didn't find a valid System.map */ + fprintf(stderr, "Warning: Valid System.map file not found!\n"); + fprintf(stderr, "Warning: proc.psinfo.wchan_s symbol names cannot be derived!\n"); + fprintf(stderr, "Warning: Addresses will be returned for proc.psinfo.wchan_s instead!\n"); + /* Free symbol array */ + for (ix = 0; ix < ksym_a_sz; ix++) { + if (ksym_a[ix].name) + free(ksym_a[ix].name); + if (ksym_a[ix].module) + free(ksym_a[ix].module); + } + free(ksym_a); + ksym_a = NULL; + ksym_a_sz = 0; + return -1; + } + + /* scan the System map */ + if ((fp = proc_statsfile(bestpath, path, sizeof(path))) == NULL) + return -oserror(); + + ix = ksym_a_sz; + + /* Read each line in System.map */ + ksym_mismatch_count = 0; + while (fgets(inbuf, sizeof(inbuf), fp) != NULL) { + /* + * System.map lines look like this on ia32 ... + * + * c010a320 T disable_irq_nosync + * + * else on ia64 ... + * + * e002000000014c80 T disable_irq_nosync + */ + + if (strstr(inbuf, "\n") == NULL) { + fprintf(stderr, "read_sysmap: truncated System.map line [%d]: %s\n", l-1, inbuf); + continue; + } + + /* Increase array size, if necessary */ + if (ksym_a_sz < ix+1) { + ksym_a_sz += INCR_KSIZE; + ksym_tmp = (struct ksym *)realloc(ksym_a, ksym_a_sz * sizeof(struct ksym)); + if (ksym_tmp == NULL) { + free(ksym_a); + goto fail; + } + ksym_a = ksym_tmp; + } + + ip = inbuf; + /* parse over address */ + while (isxdigit((int)*ip)) ip++; + + if (!isspace((int)*ip) || ip-inbuf < 4) { + /* bad format line */ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "read_sysmap: bad addr? %c[%d] line=\"%s\"\n", *ip, (int)(ip-inbuf), inbuf); + } +#endif + continue; + } + + sscanf(inbuf, "%p", (void **)&addr); + + while (isblank((int)*ip)) ip++; + + /* Only interested in symbol types that map to code addresses, + * so: t, T, W or A + */ + if (*ip != 't' && *ip != 'T' && *ip != 'W' && *ip != 'A') + continue; + + ip++; + while (isblank((int)*ip)) ip++; + + /* next should be the symbol name */ + sp = ip++; + while (!isblank((int)*ip) && *ip != '\n') ip++; + *ip = '\0'; + + /* Determine if symbol is already in ksym array. + If so, make sure the addresses match. */ + res = find_dup_name(ix - 1, addr, sp); + if (res == KSYM_NOT_FOUND) { /* add it */ + ksym_a[ix].name = strdup(sp); + if (ksym_a[ix].name == NULL) + goto fail; + ksym_a[ix].addr = addr; + ix++; + } + else if (res == KSYM_FOUND_MISMATCH) { + if (ksym_mismatch_count++ < KSYM_MISMATCH_MAX_ALLOWED) { + /* + * ia64 function pointer descriptors make this validation + * next to useless. So only report the first + * KSYM_MISMATCH_MAX_ALLOWED mismatches found. + */ + fprintf(stderr, "Warning: mismatch for \"%s\" between System.map" + " and /proc/ksyms.\n", sp); + } + } + } + + if (ksym_mismatch_count > KSYM_MISMATCH_MAX_ALLOWED) { + fprintf(stderr, "Warning: only reported first %d out of %d mismatches " + "between System.map and /proc/ksyms.\n", + KSYM_MISMATCH_MAX_ALLOWED, ksym_mismatch_count); + } + + /* release unused ksym array entries */ + ksym_tmp = (struct ksym *)realloc(ksym_a, ix * sizeof(struct ksym)); + if (ksym_tmp == NULL) { + free(ksym_a); + goto fail; + } + ksym_a = ksym_tmp; + ksym_a_sz = ix; + + qsort(ksym_a, ksym_a_sz, sizeof(struct ksym), ksym_compare_addr); + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "symbols from ksyms + sysmap ...\n"); + for (ix = 0; ix < ksym_a_sz; ix++) { + fprintf(stderr, "ksym[%d] " PRINTF_P_PFX "%p %s", ix, (void *)ksym_a[ix].addr, ksym_a[ix].name); + if (ksym_a[ix].module != NULL) fprintf(stderr, " [%s]", ksym_a[ix].module); + fprintf(stderr, "\n"); + } + } +#endif + + fclose(fp); + + return ksym_a_sz; + +fail: + e = -oserror(); + if (fp) + fclose(fp); + return e; +} + +void +read_ksym_sources(const char *release) +{ + __psint_t end_addr; + + if (read_ksyms(&end_addr) > 0) /* read /proc/ksyms first */ + read_sysmap(release, end_addr); /* then System.map */ +} diff --git a/src/pmdas/linux_proc/ksym.h b/src/pmdas/linux_proc/ksym.h new file mode 100644 index 0000000..f328ca4 --- /dev/null +++ b/src/pmdas/linux_proc/ksym.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) International Business Machines Corp., 2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This code contributed by Mike Mason (mmlnx@us.ibm.com) + */ +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#define INIT_KSIZE 8192 +#define INCR_KSIZE 2048 + +#define KSYM_FOUND_MISMATCH -1 +#define KSYM_NOT_FOUND 0 +#define KSYM_FOUND 1 + +#define KSYM_MISMATCH_MAX_ALLOWED 10 + +struct ksym { + __psint_t addr; + char *name; + char *module; +}; + +extern char *wchan(__psint_t); +extern void read_ksym_sources(const char *); + diff --git a/src/pmdas/linux_proc/linux_proc_migrate.conf b/src/pmdas/linux_proc/linux_proc_migrate.conf new file mode 100644 index 0000000..51190da --- /dev/null +++ b/src/pmdas/linux_proc/linux_proc_migrate.conf @@ -0,0 +1,55 @@ +# Copyright 2012 Red Hat, Inc. All Rights Reserved +# +# pmlogrewrite configuration for migrating archives containing proc metrics +# that were captured prior to the proc PMDA split-off from the Linux PMDA. +# +# Basically, the PMID domain changed from 60 (linux) to 3 (proc) but all +# cluster and item numbers remain unchanged. +# +# Note that the CPU indom is not migrated, even though it is +# used for cgroup.groups.cpuacct.[.]usage_percpu and +# cgroup.groups.cpuacct.usage_percpu because these metrics use a +# the dynamic pmns. To migrate archives containing these metrics, +# a script would be needed to generate the pmlogwrite config based +# on the metric names actually present in the source archive. + +# +# Migrate instance domains +indom 60.9 { indom -> 3.9 } # per-process indom +indom 60.20 { indom -> 3.20 } # cgroup hierarchy indom +indom 60.21 { indom -> 3.21 } # cgroup mount subsys indom + +# +# Migrate the pmid domain for each cluster +metric 60.8.* { pmid -> 3.*.* } # CLUSTER_PID_STAT +metric 60.9.* { pmid -> 3.*.* } # CLUSTER_PID_STATM +metric 60.13.* { pmid -> 3.*.* } # CLUSTER_PROC_RUNQ +metric 60.24.* { pmid -> 3.*.* } # CLUSTER_PID_STATUS +metric 60.31.* { pmid -> 3.*.* } # CLUSTER_PID_SCHEDSTAT +metric 60.32.* { pmid -> 3.*.* } # CLUSTER_PID_IO +metric 60.51.* { pmid -> 3.*.* } # CLUSTER_PID_FD +metric 60.37.* { pmid -> 3.*.* } # CLUSTER_CGROUP_SUBSYS +metric 60.38.* { pmid -> 3.*.* } # CLUSTER_CGROUP_MOUNTS +metric 60.39.* { pmid -> 3.*.* } # CLUSTER_CPUSET_GROUPS +metric 60.40.* { pmid -> 3.*.* } # CLUSTER_CPUSET_PROCS +metric 60.41.* { pmid -> 3.*.* } # CLUSTER_CPUACCT_GROUPS +metric 60.42.* { pmid -> 3.*.* } # CLUSTER_CPUACCT_PROCS +metric 60.43.* { pmid -> 3.*.* } # CLUSTER_CPUSCHED_GROUPS +metric 60.44.* { pmid -> 3.*.* } # CLUSTER_CPUSCHED_PROCS +metric 60.45.* { pmid -> 3.*.* } # CLUSTER_MEMORY_GROUPS +metric 60.46.* { pmid -> 3.*.* } # CLUSTER_MEMORY_PROCS +metric 60.47.* { pmid -> 3.*.* } # CLUSTER_NET_CLS_GROUPS +metric 60.48.* { pmid -> 3.*.* } # CLUSTER_NET_CLS_PROCS + +# +# These two proc.io metrics were incorrectly classified +# +metric proc.io.rchar { + sem -> counter + units -> 1,0,0,BYTE,0,0 +} + +metric proc.io.wchar { + sem -> counter + units -> 1,0,0,BYTE,0,0 +} diff --git a/src/pmdas/linux_proc/pmda.c b/src/pmdas/linux_proc/pmda.c new file mode 100644 index 0000000..2d40a54 --- /dev/null +++ b/src/pmdas/linux_proc/pmda.c @@ -0,0 +1,1896 @@ +/* + * proc PMDA + * + * Copyright (c) 2000,2004,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * Portions Copyright (c) 2002 International Business Machines Corp. + * Portions Copyright (c) 2007-2011 Aconex. All Rights Reserved. + * Portions Copyright (c) 2012-2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "contexts.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../linux/convert.h" +#include "clusters.h" +#include "indom.h" + +#include "getinfo.h" +#include "proc_pid.h" +#include "proc_runq.h" +#include "ksym.h" +#include "cgroups.h" + +/* globals */ +static int _isDSO = 1; /* for local contexts */ +static proc_pid_t proc_pid; +static struct utsname kernel_uname; +static proc_runq_t proc_runq; +static int all_access; /* =1 no access checks */ +static int have_access; /* =1 recvd uid/gid */ +static size_t _pm_system_pagesize; +static unsigned int threads; /* control.all.threads */ +static char * cgroups; /* control.all.cgroups */ + +char *proc_statspath = ""; /* optional path prefix for all stats files */ + +/* + * The proc instance domain table is direct lookup and sparse. + * It is initialized in proc_init(), see below. + */ +static pmdaIndom indomtab[NUM_INDOMS]; + +/* + * all metrics supported in this PMDA - one table entry for each + */ +static pmdaMetric metrictab[] = { + +/* + * proc//stat cluster + */ + +/* proc.nprocs */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,99), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.pid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,0), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.cmd */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,1), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.sname */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,2), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.ppid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,3), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.pgrp */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,4), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.session */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,5), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.tty */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,6), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.tty_pgrp */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,7), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.flags */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,8), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.minflt */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,9), PM_TYPE_U32, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.psinfo.cmin_flt */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,10), PM_TYPE_U32, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.psinfo.maj_flt */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,11), PM_TYPE_U32, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.psinfo.cmaj_flt */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,12), PM_TYPE_U32, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.psinfo.utime */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,13), KERNEL_ULONG, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + +/* proc.psinfo.stime */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,14), KERNEL_ULONG, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + +/* proc.psinfo.cutime */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,15), KERNEL_ULONG, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + +/* proc.psinfo.cstime */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,16), KERNEL_ULONG, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + +/* proc.psinfo.priority */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,17), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.nice */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,18), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +#if 0 +/* invalid field */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,19), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, +#endif + +/* proc.psinfo.it_real_value */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,20), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.start_time */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,21), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) } }, + +/* proc.psinfo.vsize */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,22), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.psinfo.rss */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,23), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.psinfo.rss_rlim */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,24), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.psinfo.start_code */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,25), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.end_code */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,26), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.start_stack */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,27), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.esp */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,28), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.eip */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,29), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.signal */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,30), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.blocked */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,31), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.sigignore */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,32), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.sigcatch */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,33), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.wchan */ +#if defined(HAVE_64BIT_PTR) + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,34), PM_TYPE_U64, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, +#elif defined(HAVE_32BIT_PTR) + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,34), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, +#else + error! unsupported pointer size +#endif + +/* proc.psinfo.nswap */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,35), PM_TYPE_U32, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.psinfo.cnswap */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,36), PM_TYPE_U32, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.psinfo.exit_signal */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,37), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.processor -- added by Mike Mason */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,38), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.ttyname */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,39), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + +/* proc.psinfo.wchan_s -- added by Mike Mason */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,40), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.psargs -- modified by Mike Mason */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STAT,41), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* + * proc//status cluster + * Cluster added by Mike Mason + */ + +/* proc.id.uid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,0), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.euid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,1), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.suid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,2), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.fsuid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,3), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.gid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,4), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.egid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,5), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.sgid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,6), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.fsgid */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,7), PM_TYPE_U32, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.uid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,8), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.euid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,9), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.suid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,10), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.fsuid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,11), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.gid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,12), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.egid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,13), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.sgid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,14), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.id.fsgid_nm */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,15), PM_TYPE_STRING, PROC_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.signal_s */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,16), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.blocked_s */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,17), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.sigignore_s */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,18), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.sigcatch_s */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,19), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.memory.vmsize */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,20), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmlock */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,21), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmrss */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,22), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmdata */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,23), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmstack */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,24), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmexe */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,25), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmlib */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,26), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.memory.vmswap */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,27), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + +/* proc.psinfo.threads */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATUS,28), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.cgroups */ + { NULL, + { PMDA_PMID(CLUSTER_PID_CGROUP,0), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* proc.psinfo.labels */ + { NULL, + { PMDA_PMID(CLUSTER_PID_LABEL,0), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + + +/* + * proc//statm cluster + */ + +/* proc.memory.size */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,0), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.rss */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,1), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.share */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,2), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.textrss */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,3), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.librss */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,4), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.datrss */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,5), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.dirty */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,6), PM_TYPE_U32, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + +/* proc.memory.maps -- added by Mike Mason */ + { NULL, + { PMDA_PMID(CLUSTER_PID_STATM,7), PM_TYPE_STRING, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0)}}, + +/* + * proc//schedstat cluster + */ + +/* proc.schedstat.cpu_time */ + { NULL, + { PMDA_PMID(CLUSTER_PID_SCHEDSTAT,0), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0)}}, +/* proc.schedstat.run_delay */ + { NULL, + { PMDA_PMID(CLUSTER_PID_SCHEDSTAT,1), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0)}}, +/* proc.schedstat.pcount */ + { NULL, + { PMDA_PMID(CLUSTER_PID_SCHEDSTAT,2), KERNEL_ULONG, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE)}}, + +/* + * proc//io cluster + */ +/* proc.io.rchar */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,0), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, +/* proc.io.wchar */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,1), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, +/* proc.io.syscr */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,2), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE)}}, +/* proc.io.syscw */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,3), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE)}}, +/* proc.io.read_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,4), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, +/* proc.io.write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,5), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, +/* proc.io.cancelled_write_bytes */ + { NULL, + { PMDA_PMID(CLUSTER_PID_IO,6), PM_TYPE_U64, PROC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, + +/* + * proc.runq cluster + */ + +/* proc.runq.runnable */ + { &proc_runq.runnable, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.blocked */ + { &proc_runq.blocked, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.sleeping */ + { &proc_runq.sleeping, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.stopped */ + { &proc_runq.stopped, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.swapped */ + { &proc_runq.swapped, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.defunct */ + { &proc_runq.defunct, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.unknown */ + { &proc_runq.unknown, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 6), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* proc.runq.kernel */ + { &proc_runq.kernel, + { PMDA_PMID(CLUSTER_PROC_RUNQ, 7), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* + * control groups cluster + */ + /* cgroups.subsys.hierarchy */ + { NULL, {PMDA_PMID(CLUSTER_CGROUP_SUBSYS,0), PM_TYPE_U32, + CGROUP_SUBSYS_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* cgroups.subsys.count */ + { NULL, {PMDA_PMID(CLUSTER_CGROUP_SUBSYS,1), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroups.mounts.subsys */ + { NULL, {PMDA_PMID(CLUSTER_CGROUP_MOUNTS,0), PM_TYPE_STRING, + CGROUP_MOUNTS_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* cgroups.mounts.count */ + { NULL, {PMDA_PMID(CLUSTER_CGROUP_MOUNTS,1), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.cpuset.[.]cpus */ + { NULL, {PMDA_PMID(CLUSTER_CPUSET_GROUPS,0), PM_TYPE_STRING, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* cgroup.groups.cpuset.[.]mems */ + { NULL, {PMDA_PMID(CLUSTER_CPUSET_GROUPS,0), PM_TYPE_STRING, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* cgroup.groups.cpuacct.[.]stat.user */ + { NULL, {PMDA_PMID(CLUSTER_CPUACCT_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + /* cgroup.groups.cpuacct.[.]stat.system */ + { NULL, {PMDA_PMID(CLUSTER_CPUACCT_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + /* cgroup.groups.cpuacct.[.]usage */ + { NULL, {PMDA_PMID(CLUSTER_CPUACCT_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.cpuacct.[.]usage_percpu */ + { NULL, {PMDA_PMID(CLUSTER_CPUACCT_GROUPS,0), PM_TYPE_U64, + CPU_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.cpusched.[.]shares */ + { NULL, {PMDA_PMID(CLUSTER_CPUSCHED_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,0,0,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.cache */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.rss */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.rss_huge */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.mapped_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.writeback */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.swap */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.pgpgin */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.pgpgout */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.pgfault */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.pgmajfault */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.inactive_anon */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.active_anon */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.inactive_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.active_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.unevictable */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_cache */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_rss */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_rss_huge */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_mapped_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_writeback */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_swap */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_pgpgin */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_pgpgout */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_pgfault */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_pgmajfault */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_inactive_anon */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_active_anon */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_inactive_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_active_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.total_unevictable */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.recent_rotated_anon */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.recent_rotated_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.recent_scanned_anon */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.memory.[.]stat.recent_scanned_file */ + { NULL, {PMDA_PMID(CLUSTER_MEMORY_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.netclass.[.]classid */ + { NULL, {PMDA_PMID(CLUSTER_NET_CLS_GROUPS,0), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.blkio.[.]io_merged.read */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_merged.write */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_merged.sync */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_merged.async */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_merged.total */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_queued.read */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_queued.write */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_queued.sync */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_queued.async */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_queued.total */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_service_bytes.read */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_bytes.write */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_bytes.sync */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_bytes.async */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_bytes.total */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, + + /* cgroup.groups.blkio.[.]io_serviced.read */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_serviced.write */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_serviced.sync */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_serviced.async */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_serviced.total */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]io_service_time.read */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_time.write */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_time.sync */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_time.async */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_service_time.total */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_wait_time.read */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_wait_time.write */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_wait_time.sync */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_wait_time.async */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]io_wait_time.total */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, }, + + /* cgroup.groups.blkio.[.]sectors */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + + /* cgroup.groups.blkio.[.]time */ + { NULL, {PMDA_PMID(CLUSTER_BLKIO_GROUPS,0), PM_TYPE_U64, + DISK_INDOM, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, }, + + +/* + * proc//fd cluster + */ + + /* proc.fd.count */ + { NULL, { PMDA_PMID(CLUSTER_PID_FD,0), PM_TYPE_U32, + PROC_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + +/* + * Metrics control cluster + */ + + /* proc.control.all.threads */ + { &threads, { PMDA_PMID(CLUSTER_CONTROL, 1), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) } }, + /* proc.control.perclient.threads */ + { NULL, { PMDA_PMID(CLUSTER_CONTROL, 2), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) } }, + /* proc.control.perclient.cgroups */ + { NULL, { PMDA_PMID(CLUSTER_CONTROL, 3), PM_TYPE_STRING, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) } }, +}; + +pmInDom +proc_indom(int serial) +{ + return indomtab[serial].it_indom; +} + +FILE * +proc_statsfile(const char *path, char *buffer, int size) +{ + snprintf(buffer, size, "%s%s", proc_statspath, path); + buffer[size-1] = '\0'; + return fopen(buffer, "r"); +} + +static void +proc_refresh(pmdaExt *pmda, int *need_refresh) +{ + int need_refresh_mtab = 0; + + if (need_refresh[CLUSTER_CPUACCT_GROUPS]) + refresh_cgroup_cpus(INDOM(CPU_INDOM)); + + if (need_refresh[CLUSTER_CGROUP_SUBSYS] || + need_refresh[CLUSTER_CGROUP_MOUNTS] || + need_refresh[CLUSTER_CPUSET_GROUPS] || + need_refresh[CLUSTER_CPUACCT_GROUPS] || + need_refresh[CLUSTER_CPUSCHED_GROUPS] || + need_refresh[CLUSTER_BLKIO_GROUPS] || + need_refresh[CLUSTER_NET_CLS_GROUPS] || + need_refresh[CLUSTER_MEMORY_GROUPS]) { + refresh_cgroup_subsys(INDOM(CGROUP_SUBSYS_INDOM)); + need_refresh_mtab |= refresh_cgroups(pmda, NULL); + } + + if (need_refresh_mtab) + pmdaDynamicMetricTable(pmda); + + if (need_refresh[CLUSTER_PID_STAT] || + need_refresh[CLUSTER_PID_STATM] || + need_refresh[CLUSTER_PID_STATUS] || + need_refresh[CLUSTER_PID_IO] || + need_refresh[CLUSTER_PID_LABEL] || + need_refresh[CLUSTER_PID_CGROUP] || + need_refresh[CLUSTER_PID_SCHEDSTAT] || + need_refresh[CLUSTER_PID_FD]) { + refresh_proc_pid(&proc_pid, + proc_ctx_threads(pmda->e_context, threads), + proc_ctx_cgroups(pmda->e_context, cgroups)); + } + + if (need_refresh[CLUSTER_PROC_RUNQ]) + refresh_proc_runq(&proc_runq); +} + +static int +proc_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + __pmInDom_int *indomp = (__pmInDom_int *)&indom; + int need_refresh[NUM_CLUSTERS] = { 0 }; + char newname[16]; /* see Note below */ + int sts; + + switch (indomp->serial) { + case CPU_INDOM: + /* + * Used by cgroup.groups.cpuacct.[.]usage_percpu + * and cgroup.groups.cpuacct.usage_percpu + */ + need_refresh[CLUSTER_CPUACCT_GROUPS]++; + break; + case DISK_INDOM: + need_refresh[CLUSTER_BLKIO_GROUPS]++; + break; + case PROC_INDOM: + need_refresh[CLUSTER_PID_STAT]++; + need_refresh[CLUSTER_PID_STATM]++; + need_refresh[CLUSTER_PID_STATUS]++; + need_refresh[CLUSTER_PID_LABEL]++; + need_refresh[CLUSTER_PID_CGROUP]++; + need_refresh[CLUSTER_PID_SCHEDSTAT]++; + need_refresh[CLUSTER_PID_IO]++; + need_refresh[CLUSTER_PID_FD]++; + break; + case CGROUP_SUBSYS_INDOM: + need_refresh[CLUSTER_CGROUP_SUBSYS]++; + break; + case CGROUP_MOUNTS_INDOM: + need_refresh[CLUSTER_CGROUP_MOUNTS]++; + break; + /* no default label : pmdaInstance will pick up errors */ + } + + if (indomp->serial == PROC_INDOM && inst == PM_IN_NULL && name != NULL) { + /* + * For the proc indom, if the name is a pid (as a string), and it + * contains only digits (i.e. it's not a full instance name) then + * reformat it to be exactly six digits, with leading zeros. + * + * Note that although format %06d is used here and in proc_pid.c, + * the pid could be longer than this (in which case there + * are no leading zeroes. The size of newname[] is chosen + * to comfortably accommodate a 32-bit pid (Linux maximum), + * or max value of 4294967295 (10 digits) + */ + char *p; + for (p = name; *p != '\0'; p++) { + if (!isdigit((int)*p)) + break; + } + if (*p == '\0') { + snprintf(newname, sizeof(newname), "%06d", atoi(name)); + name = newname; + } + } + + sts = PM_ERR_PERMISSION; + have_access = proc_ctx_access(pmda->e_context) || all_access; + if (have_access || indomp->serial != PROC_INDOM) { + proc_refresh(pmda, need_refresh); + sts = pmdaInstance(indom, inst, name, result, pmda); + } + have_access = proc_ctx_revert(pmda->e_context); + + return sts; +} + +/* + * callback provided to pmdaFetch + */ + +static int +proc_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int cluster = proc_pmid_cluster(mdesc->m_desc.pmid); + int sts; + unsigned long ul; + const char *cp; + char *f; + int *ip; + proc_pid_entry_t *entry; + void *fsp; + static long hz = -1; + char *tail; + + if (hz == -1) + hz = sysconf(_SC_CLK_TCK); + + if (mdesc->m_user != NULL) { + /* + * The metric value is extracted directly via the address specified + * in metrictab. Note: not all metrics support this - those that + * don't have NULL for the m_user field in their respective + * metrictab slot. + */ + + switch (mdesc->m_desc.type) { + case PM_TYPE_32: + atom->l = *(__int32_t *)mdesc->m_user; + break; + case PM_TYPE_U32: + atom->ul = *(__uint32_t *)mdesc->m_user; + break; + case PM_TYPE_64: + atom->ll = *(__int64_t *)mdesc->m_user; + break; + case PM_TYPE_U64: + atom->ull = *(__uint64_t *)mdesc->m_user; + break; + case PM_TYPE_FLOAT: + atom->f = *(float *)mdesc->m_user; + break; + case PM_TYPE_DOUBLE: + atom->d = *(double *)mdesc->m_user; + break; + case PM_TYPE_STRING: + cp = *(char **)mdesc->m_user; + atom->cp = (char *)(cp ? cp : ""); + break; + default: + return 0; + } + } + else + switch (cluster) { + + case CLUSTER_PID_STAT: + if (idp->item == 99) /* proc.nprocs */ + atom->ul = proc_pid.indom->it_numinst; + else { + static char ttyname[MAXPATHLEN]; + + if (!have_access) + return PM_ERR_PERMISSION; + if ((entry = fetch_proc_pid_stat(inst, &proc_pid)) == NULL) + return PM_ERR_INST; + + switch (idp->item) { + + + case PROC_PID_STAT_PID: + atom->ul = entry->id; + break; + + case PROC_PID_STAT_TTYNAME: + if ((f = _pm_getfield(entry->stat_buf, PROC_PID_STAT_TTY)) == NULL) + atom->cp = "?"; + else { + dev_t dev = (dev_t)atoi(f); + atom->cp = get_ttyname_info(inst, dev, ttyname); + } + break; + + case PROC_PID_STAT_CMD: + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->cp = f + 1; + atom->cp[strlen(atom->cp)-1] = '\0'; + break; + + case PROC_PID_STAT_PSARGS: + atom->cp = entry->name + 7; + break; + + case PROC_PID_STAT_STATE: + /* + * string + */ + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->cp = f; + break; + + case PROC_PID_STAT_VSIZE: + case PROC_PID_STAT_RSS_RLIM: + /* + * bytes converted to kbytes + */ + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + atom->ul /= 1024; + break; + + case PROC_PID_STAT_RSS: + /* + * pages converted to kbytes + */ + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + atom->ul *= _pm_system_pagesize / 1024; + break; + + case PROC_PID_STAT_UTIME: + case PROC_PID_STAT_STIME: + case PROC_PID_STAT_CUTIME: + case PROC_PID_STAT_CSTIME: + /* + * unsigned jiffies converted to unsigned milliseconds + */ + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + + ul = (__uint32_t)strtoul(f, &tail, 0); + _pm_assign_ulong(atom, 1000 * (double)ul / hz); + break; + + case PROC_PID_STAT_PRIORITY: + case PROC_PID_STAT_NICE: + /* + * signed decimal int + */ + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->l = (__int32_t)strtol(f, &tail, 0); + break; + + case PROC_PID_STAT_WCHAN: + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; +#if defined(HAVE_64BIT_PTR) + atom->ull = (__uint64_t)strtoull(f, &tail, 0); +#else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); +#endif + break; + + case PROC_PID_STAT_WCHAN_SYMBOL: + if (entry->wchan_buf) /* 2.6 kernel, /proc//wchan */ + atom->cp = entry->wchan_buf; + else { /* old school (2.4 kernels, at least) */ + char *wc; + /* + * Convert address to symbol name if requested + * Added by Mike Mason + */ + f = _pm_getfield(entry->stat_buf, PROC_PID_STAT_WCHAN); + if (f == NULL) + return PM_ERR_INST; +#if defined(HAVE_64BIT_PTR) + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + if ((wc = wchan(atom->ull))) + atom->cp = wc; + else + atom->cp = atom->ull ? f : ""; +#else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + if ((wc = wchan((__psint_t)atom->ul))) + atom->cp = wc; + else + atom->cp = atom->ul ? f : ""; +#endif + } + break; + + default: + /* + * unsigned decimal int + */ + if (idp->item < NR_PROC_PID_STAT) { + if ((f = _pm_getfield(entry->stat_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + } + else + return PM_ERR_PMID; + break; + } + } + break; + + case CLUSTER_PID_STATM: + if (!have_access) + return PM_ERR_PERMISSION; + if (idp->item == PROC_PID_STATM_MAPS) { /* proc.memory.maps */ + if ((entry = fetch_proc_pid_maps(inst, &proc_pid)) == NULL) + return PM_ERR_INST; + atom->cp = entry->maps_buf; + } else { + if ((entry = fetch_proc_pid_statm(inst, &proc_pid)) == NULL) + return PM_ERR_INST; + + if (idp->item <= PROC_PID_STATM_DIRTY) { + /* unsigned int */ + if ((f = _pm_getfield(entry->statm_buf, idp->item)) == NULL) + return PM_ERR_INST; + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + atom->ul *= _pm_system_pagesize / 1024; + } + else + return PM_ERR_PMID; + } + break; + + case CLUSTER_PID_SCHEDSTAT: + if (!have_access) + return PM_ERR_PERMISSION; + if ((entry = fetch_proc_pid_schedstat(inst, &proc_pid)) == NULL) + return (oserror() == ENOENT) ? PM_ERR_APPVERSION : PM_ERR_INST; + + if (idp->item < NR_PROC_PID_SCHED) { + if ((f = _pm_getfield(entry->schedstat_buf, idp->item)) == NULL) + return PM_ERR_INST; + if (idp->item == PROC_PID_SCHED_PCOUNT && + mdesc->m_desc.type == PM_TYPE_U32) + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + else +#if defined(HAVE_64BIT_PTR) + atom->ull = (__uint64_t)strtoull(f, &tail, 0); +#else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); +#endif + } + else + return PM_ERR_PMID; + break; + + case CLUSTER_PID_IO: + if (!have_access) + return PM_ERR_PERMISSION; + if ((entry = fetch_proc_pid_io(inst, &proc_pid)) == NULL) + return (oserror() == ENOENT) ? PM_ERR_APPVERSION : PM_ERR_INST; + + switch (idp->item) { + + case PROC_PID_IO_RCHAR: + if ((f = _pm_getfield(entry->io_lines.rchar, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + case PROC_PID_IO_WCHAR: + if ((f = _pm_getfield(entry->io_lines.wchar, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + case PROC_PID_IO_SYSCR: + if ((f = _pm_getfield(entry->io_lines.syscr, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + case PROC_PID_IO_SYSCW: + if ((f = _pm_getfield(entry->io_lines.syscw, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + case PROC_PID_IO_READ_BYTES: + if ((f = _pm_getfield(entry->io_lines.readb, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + case PROC_PID_IO_WRITE_BYTES: + if ((f = _pm_getfield(entry->io_lines.writeb, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + case PROC_PID_IO_CANCELLED_BYTES: + if ((f = _pm_getfield(entry->io_lines.cancel, 1)) == NULL) + atom->ull = 0; + else + atom->ull = (__uint64_t)strtoull(f, &tail, 0); + break; + + default: + return PM_ERR_PMID; + } + break; + + /* + * Cluster added by Mike Mason + */ + case CLUSTER_PID_STATUS: + if (!have_access) + return PM_ERR_PERMISSION; + if ((entry = fetch_proc_pid_status(inst, &proc_pid)) == NULL) + return PM_ERR_INST; + + switch (idp->item) { + + case PROC_PID_STATUS_UID: + case PROC_PID_STATUS_EUID: + case PROC_PID_STATUS_SUID: + case PROC_PID_STATUS_FSUID: + case PROC_PID_STATUS_UID_NM: + case PROC_PID_STATUS_EUID_NM: + case PROC_PID_STATUS_SUID_NM: + case PROC_PID_STATUS_FSUID_NM: + { + struct passwd *pwe; + + if ((f = _pm_getfield(entry->status_lines.uid, (idp->item % 4) + 1)) == NULL) + return PM_ERR_INST; + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + if (idp->item > PROC_PID_STATUS_FSUID) { + if ((pwe = getpwuid((uid_t)atom->ul)) != NULL) + atom->cp = pwe->pw_name; + else + atom->cp = "UNKNOWN"; + } + } + break; + + case PROC_PID_STATUS_GID: + case PROC_PID_STATUS_EGID: + case PROC_PID_STATUS_SGID: + case PROC_PID_STATUS_FSGID: + case PROC_PID_STATUS_GID_NM: + case PROC_PID_STATUS_EGID_NM: + case PROC_PID_STATUS_SGID_NM: + case PROC_PID_STATUS_FSGID_NM: + { + struct group *gre; + + if ((f = _pm_getfield(entry->status_lines.gid, (idp->item % 4) + 1)) == NULL) + return PM_ERR_INST; + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + if (idp->item > PROC_PID_STATUS_FSGID) { + if ((gre = getgrgid((gid_t)atom->ul)) != NULL) { + atom->cp = gre->gr_name; + } else { + atom->cp = "UNKNOWN"; + } + } + } + break; + + case PROC_PID_STATUS_SIGNAL: + if ((atom->cp = _pm_getfield(entry->status_lines.sigpnd, 1)) == NULL) + return PM_ERR_INST; + break; + + case PROC_PID_STATUS_BLOCKED: + if ((atom->cp = _pm_getfield(entry->status_lines.sigblk, 1)) == NULL) + return PM_ERR_INST; + break; + + case PROC_PID_STATUS_SIGCATCH: + if ((atom->cp = _pm_getfield(entry->status_lines.sigcgt, 1)) == NULL) + return PM_ERR_INST; + break; + + case PROC_PID_STATUS_SIGIGNORE: + if ((atom->cp = _pm_getfield(entry->status_lines.sigign, 1)) == NULL) + return PM_ERR_INST; + break; + + case PROC_PID_STATUS_VMSIZE: + if ((f = _pm_getfield(entry->status_lines.vmsize, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMLOCK: + if ((f = _pm_getfield(entry->status_lines.vmlck, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMRSS: + if ((f = _pm_getfield(entry->status_lines.vmrss, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMDATA: + if ((f = _pm_getfield(entry->status_lines.vmdata, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMSTACK: + if ((f = _pm_getfield(entry->status_lines.vmstk, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMEXE: + if ((f = _pm_getfield(entry->status_lines.vmexe, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMLIB: + if ((f = _pm_getfield(entry->status_lines.vmlib, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_VMSWAP: + if ((f = _pm_getfield(entry->status_lines.vmswap, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + case PROC_PID_STATUS_THREADS: + if ((f = _pm_getfield(entry->status_lines.threads, 1)) == NULL) + atom->ul = 0; + else + atom->ul = (__uint32_t)strtoul(f, &tail, 0); + break; + + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_CGROUP_SUBSYS: + switch (idp->item) { + case 0: /* cgroup.subsys.hierarchy */ + sts = pmdaCacheLookup(INDOM(CGROUP_SUBSYS_INDOM), inst, NULL, (void **)&ip); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + atom->ul = *ip; + break; + + case 1: /* cgroup.subsys.count */ + atom->ul = pmdaCacheOp(INDOM(CGROUP_SUBSYS_INDOM), PMDA_CACHE_SIZE_ACTIVE); + break; + } + break; + + case CLUSTER_CGROUP_MOUNTS: + switch (idp->item) { + case 0: /* cgroup.mounts.subsys */ + sts = pmdaCacheLookup(INDOM(CGROUP_MOUNTS_INDOM), inst, NULL, &fsp); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + atom->cp = cgroup_find_subsys(INDOM(CGROUP_SUBSYS_INDOM), fsp); + break; + + case 1: /* cgroup.mounts.count */ + atom->ul = pmdaCacheOp(INDOM(CGROUP_MOUNTS_INDOM), PMDA_CACHE_SIZE_ACTIVE); + break; + } + break; + + case CLUSTER_CPUSET_GROUPS: + case CLUSTER_CPUACCT_GROUPS: + case CLUSTER_CPUSCHED_GROUPS: + case CLUSTER_MEMORY_GROUPS: + case CLUSTER_NET_CLS_GROUPS: + case CLUSTER_BLKIO_GROUPS: + return cgroup_group_fetch(mdesc->m_desc.pmid, inst, atom); + + case CLUSTER_PID_FD: + if (!have_access) + return PM_ERR_PERMISSION; + if (idp->item > PROC_PID_FD_COUNT) + return PM_ERR_PMID; + if ((entry = fetch_proc_pid_fd(inst, &proc_pid)) == NULL) + return PM_ERR_INST; + atom->ul = entry->fd_count; + break; + + case CLUSTER_PID_CGROUP: + if (!have_access) + return PM_ERR_PERMISSION; + if (idp->item > PROC_PID_CGROUP) + return PM_ERR_PMID; + if ((entry = fetch_proc_pid_cgroup(inst, &proc_pid)) == NULL) { + if (oserror() == ENOENT) return PM_ERR_APPVERSION; + if (oserror() != ENODATA) return PM_ERR_INST; + atom->cp = ""; + } else { + atom->cp = proc_strings_lookup(entry->cgroup_id); + } + break; + + case CLUSTER_PID_LABEL: + if (!have_access) + return PM_ERR_PERMISSION; + if (idp->item > PROC_PID_LABEL) + return PM_ERR_PMID; + if ((entry = fetch_proc_pid_label(inst, &proc_pid)) == NULL) { + if (oserror() == ENOENT) return PM_ERR_APPVERSION; + if (oserror() != ENODATA) return PM_ERR_INST; + atom->cp = ""; + } else { + atom->cp = proc_strings_lookup(entry->label_id); + } + break; + + case CLUSTER_CONTROL: + switch (idp->item) { + /* case 1: not reached -- proc.control.all.threads is direct */ + case 2: /* proc.control.perclient.threads */ + atom->ul = proc_ctx_threads(pmdaGetContext(), threads); + break; + case 3: /* proc.control.perclient.cgroups */ + cp = proc_ctx_cgroups(pmdaGetContext(), cgroups); + atom->cp = (char *)(cp ? cp : ""); + break; + default: + return PM_ERR_PMID; + } + break; + + default: /* unknown cluster */ + return PM_ERR_PMID; + } + + return PMDA_FETCH_STATIC; +} + +static int +proc_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i, sts, cluster; + int need_refresh[NUM_CLUSTERS] = { 0 }; + + for (i = 0; i < numpmid; i++) { + cluster = proc_pmid_cluster(pmidlist[i]); + if (cluster >= MIN_CLUSTER && cluster < NUM_CLUSTERS) + need_refresh[cluster]++; + } + + have_access = proc_ctx_access(pmda->e_context) || all_access; + proc_refresh(pmda, need_refresh); + sts = pmdaFetch(numpmid, pmidlist, resp, pmda); + have_access = proc_ctx_revert(pmda->e_context); + return sts; +} + +static int +proc_store(pmResult *result, pmdaExt *pmda) +{ + int i, sts = 0; + + have_access = proc_ctx_access(pmda->e_context) || all_access; + + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + __pmID_int *idp = (__pmID_int *)&(vsp->pmid); + pmAtomValue av; + + if (idp->cluster != CLUSTER_CONTROL) + sts = PM_ERR_PERMISSION; + else if (vsp->numval != 1) + sts = PM_ERR_INST; + else switch (idp->item) { + case 1: /* proc.control.all.threads */ + if (!have_access) + sts = PM_ERR_PERMISSION; + else if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_U32, &av, PM_TYPE_U32)) >= 0) { + if (av.ul > 1) /* only zero or one allowed */ + sts = PM_ERR_CONV; + else + threads = av.ul; + } + break; + case 2: /* proc.control.perclient.threads */ + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_U32, &av, PM_TYPE_U32)) >= 0) { + sts = proc_ctx_set_threads(pmda->e_context, av.ul); + } + break; + case 3: /* proc.control.perclient.cgroups */ + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_STRING, &av, PM_TYPE_STRING)) >= 0) { + if ((sts = proc_ctx_set_cgroups(pmda->e_context, av.cp)) < 0) + free(av.cp); + } + break; + default: + sts = PM_ERR_PERMISSION; + } + if (sts < 0) + break; + } + + have_access = proc_ctx_revert(pmda->e_context); + return sts; +} + +static int +proc_text(int ident, int type, char **buf, pmdaExt *pmda) +{ + if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) { + int sts = pmdaDynamicLookupText(ident, type, buf, pmda); + if (sts != -ENOENT) + return sts; + } + return pmdaText(ident, type, buf, pmda); +} + +static int +proc_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + pmdaNameSpace *tree = pmdaDynamicLookupName(pmda, name); + if (tree == NULL) + return PM_ERR_NAME; + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "proc_pmid: name=%s tree:\n", name); + __pmDumpNameNode(stderr, tree->root, 1); + } + return pmdaTreePMID(tree, name, pmid); +} + +static int +proc_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + pmdaNameSpace *tree = pmdaDynamicLookupPMID(pmda, pmid); + if (tree == NULL) + return PM_ERR_PMID; + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "proc_name: pmid=%s tree:\n", pmIDStr(pmid)); + __pmDumpNameNode(stderr, tree->root, 1); + } + return pmdaTreeName(tree, pmid, nameset); +} + +static int +proc_children(const char *name, int flag, char ***kids, int **sts, pmdaExt *pmda) +{ + pmdaNameSpace *tree = pmdaDynamicLookupName(pmda, name); + if (tree == NULL) + return PM_ERR_NAME; + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "proc_children: name=%s flag=%d tree:\n", name, flag); + __pmDumpNameNode(stderr, tree->root, 1); + } + return pmdaTreeChildren(tree, name, flag, kids, sts); +} + +/* + * Helper routines for accessing a generic static string dictionary + */ + +char * +proc_strings_lookup(int index) +{ + char *value; + pmInDom dict = INDOM(STRINGS_INDOM); + + if (pmdaCacheLookup(dict, index, &value, NULL) == PMDA_CACHE_ACTIVE) + return value; + return ""; +} + +int +proc_strings_insert(const char *buf) +{ + pmInDom dict = INDOM(STRINGS_INDOM); + return pmdaCacheStore(dict, PMDA_CACHE_ADD, buf, NULL); +} + +/* + * Initialise the agent (both daemon and DSO). + */ + +void +__PMDA_INIT_CALL +proc_init(pmdaInterface *dp) +{ + int nindoms = sizeof(indomtab)/sizeof(indomtab[0]); + int nmetrics = sizeof(metrictab)/sizeof(metrictab[0]); + char *envpath; + + _pm_system_pagesize = getpagesize(); + if ((envpath = getenv("PROC_STATSPATH")) != NULL) + proc_statspath = envpath; + + if (_isDSO) { + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(helppath, sizeof(helppath), "%s%c" "proc" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_6, "proc DSO", helppath); + } + + if (dp->status != 0) + return; + dp->comm.flags |= PDU_FLAG_AUTH; + + dp->version.six.instance = proc_instance; + dp->version.six.store = proc_store; + dp->version.six.fetch = proc_fetch; + dp->version.six.text = proc_text; + dp->version.six.pmid = proc_pmid; + dp->version.six.name = proc_name; + dp->version.six.children = proc_children; + dp->version.six.attribute = proc_ctx_attrs; + pmdaSetEndContextCallBack(dp, proc_ctx_end); + pmdaSetFetchCallBack(dp, proc_fetchCallBack); + + /* + * Initialize the instance domain table. + */ + indomtab[CPU_INDOM].it_indom = CPU_INDOM; + indomtab[DISK_INDOM].it_indom = DISK_INDOM; + indomtab[DEVT_INDOM].it_indom = DEVT_INDOM; + indomtab[PROC_INDOM].it_indom = PROC_INDOM; + indomtab[STRINGS_INDOM].it_indom = STRINGS_INDOM; + indomtab[CGROUP_SUBSYS_INDOM].it_indom = CGROUP_SUBSYS_INDOM; + indomtab[CGROUP_MOUNTS_INDOM].it_indom = CGROUP_MOUNTS_INDOM; + + proc_pid.indom = &indomtab[PROC_INDOM]; + + /* + * Read System.map and /proc/ksyms. Used to translate wait channel + * addresses to symbol names. + * Added by Mike Mason + */ + read_ksym_sources(kernel_uname.release); + + cgroup_init(metrictab, nmetrics); + proc_ctx_init(); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, indomtab, nindoms, metrictab, nmetrics); + + /* string metrics use the pmdaCache API for value indexing */ + pmdaCacheOp(INDOM(STRINGS_INDOM), PMDA_CACHE_STRINGS); + + /* cgroup metrics use the pmdaCache API for indom indexing */ + pmdaCacheOp(INDOM(CPU_INDOM), PMDA_CACHE_CULL); + pmdaCacheOp(INDOM(DISK_INDOM), PMDA_CACHE_CULL); + pmdaCacheOp(INDOM(CGROUP_SUBSYS_INDOM), PMDA_CACHE_CULL); + pmdaCacheOp(INDOM(CGROUP_MOUNTS_INDOM), PMDA_CACHE_CULL); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + { "no-access-checks", 0, 'A', 0, "no access checks will be performed (insecure, beware!)" }, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + { "with-threads", 0, 'L', 0, "include threads in the all-processes instance domain" }, + { "from-cgroup", 1, 'r', "NAME", "restrict monitoring to processes in the named cgroup" }, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "AD:d:l:Lr:U:?", + .long_options = longopts, +}; + +int +main(int argc, char **argv) +{ + int c, sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + char *username = "root"; + + _isDSO = 0; + __pmSetProgname(argv[0]); + snprintf(helppath, sizeof(helppath), "%s%c" "proc" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_6, pmProgname, PROC, "proc.log", helppath); + + while ((c = pmdaGetOptions(argc, argv, &opts, &dispatch)) != EOF) { + switch (c) { + case 'A': + all_access = 1; + break; + case 'L': + threads = 1; + break; + case 'r': + cgroups = opts.optarg; + break; + } + } + + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + __pmSetProcessIdentity(username); + + proc_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/linux_proc/proc_pid.c b/src/pmdas/linux_proc/proc_pid.c new file mode 100644 index 0000000..152d96c --- /dev/null +++ b/src/pmdas/linux_proc/proc_pid.c @@ -0,0 +1,957 @@ +/* + * Linux proc//{stat,statm,status,...} Clusters + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2000,2004,2006 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include +#include +#include +#include "proc_pid.h" +#include "indom.h" + +static proc_pid_list_t pids; + +static int +compare_pid(const void *pa, const void *pb) +{ + int a = *(int *)pa; + int b = *(int *)pb; + return a - b; +} + +static void +pidlist_append_pid(int pid) +{ + if (pids.count >= pids.size) { + pids.size += 64; + if (!(pids.pids = (int *)realloc(pids.pids, pids.size * sizeof(int)))) { + perror("pidlist_append: out of memory"); + pids.size = pids.count = 0; + return; /* soldier on bravely */ + } + } + pids.pids[pids.count++] = pid; +} + +static void +pidlist_append(const char *pidname) +{ + pidlist_append_pid(atoi(pidname)); +} + +static void +tasklist_append(const char *pid) +{ + DIR *taskdirp; + struct dirent *tdp; + char taskpath[1024]; + + sprintf(taskpath, "%s/proc/%s/task", proc_statspath, pid); + if ((taskdirp = opendir(taskpath)) != NULL) { + while ((tdp = readdir(taskdirp)) != NULL) { + if (!isdigit((int)tdp->d_name[0]) || strcmp(pid, tdp->d_name) == 0) + continue; + pidlist_append(tdp->d_name); + } + closedir(taskdirp); + } +} + +static int +refresh_cgroup_pidlist(int want_threads, const char *cgroup) +{ + char path[MAXPATHLEN]; + FILE *fp; + int pid; + + /* + * We're running in cgroups mode where a subset of the processes is + * going to be returned based on the cgroup specified earlier via a + * store into the proc.control.{all,perclient}.cgroups metric. + * + * Use the "cgroup.procs" or "tasks" file depending on want_threads. + * Note that both these files are already sorted, ascending numeric. + */ + if (want_threads) + snprintf(path, sizeof(path), "%s%s/tasks", proc_statspath, cgroup); + else + snprintf(path, sizeof(path), "%s%s/cgroup.procs", proc_statspath, cgroup); + + if ((fp = fopen(path, "r")) != NULL) { + while (fscanf(fp, "%d\n", &pid) == 1) + pidlist_append_pid(pid); + fclose(fp); + } + return 0; +} + +static int +refresh_global_pidlist(int want_threads) +{ + DIR *dirp; + struct dirent *dp; + char path[MAXPATHLEN]; + + snprintf(path, sizeof(path), "%s/proc", proc_statspath); + if ((dirp = opendir(path)) == NULL) + return -oserror(); + + /* note: readdir on /proc ignores threads */ + while ((dp = readdir(dirp)) != NULL) { + if (isdigit((int)dp->d_name[0])) { + pidlist_append(dp->d_name); + if (want_threads) + tasklist_append(dp->d_name); + } + } + closedir(dirp); + + qsort(pids.pids, pids.count, sizeof(int), compare_pid); + return 0; +} + +static void +refresh_proc_pidlist(proc_pid_t *proc_pid) +{ + int i; + int fd; + char *p; + char buf[MAXPATHLEN]; + __pmHashNode *node, *next, *prev; + proc_pid_entry_t *ep; + pmdaIndom *indomp = proc_pid->indom; + + if (indomp->it_numinst < pids.count) + indomp->it_set = (pmdaInstid *)realloc(indomp->it_set, + pids.count * sizeof(pmdaInstid)); + indomp->it_numinst = pids.count; + + /* + * invalidate all entries so we can harvest pids that have exited + */ + for (i=0; i < proc_pid->pidhash.hsize; i++) { + for (node=proc_pid->pidhash.hash[i]; node != NULL; node = node->next) { + ep = (proc_pid_entry_t *)node->data; + ep->flags = 0; + } + } + + /* + * walk pid list and add new pids to the hash table, + * marking entries valid as we go ... + */ + for (i=0; i < pids.count; i++) { + node = __pmHashSearch(pids.pids[i], &proc_pid->pidhash); + if (node == NULL) { + int k = 0; + + ep = (proc_pid_entry_t *)malloc(sizeof(proc_pid_entry_t)); + memset(ep, 0, sizeof(proc_pid_entry_t)); + + ep->id = pids.pids[i]; + + snprintf(buf, sizeof(buf), "%s/proc/%d/cmdline", proc_statspath, pids.pids[i]); + if ((fd = open(buf, O_RDONLY)) >= 0) { + sprintf(buf, "%06d ", pids.pids[i]); + if ((k = read(fd, buf+7, sizeof(buf)-8)) > 0) { + p = buf + k +7; + *p-- = '\0'; + /* Skip trailing nils, i.e. don't replace them */ + while (buf+7 < p) { + if (*p-- != '\0') { + break; + } + } + /* Remove NULL terminators from cmdline string array */ + /* Suggested by Mike Mason */ + while (buf+7 < p) { + if (*p == '\0') *p = ' '; + p--; + } + } + close(fd); + } + + if (k == 0) { + /* + * If a process is swapped out, /proc//cmdline + * returns an empty string so we have to get it + * from /proc//status or /proc//stat + */ + sprintf(buf, "%s/proc/%d/status", proc_statspath, pids.pids[i]); + if ((fd = open(buf, O_RDONLY)) >= 0) { + /* We engage in a bit of a hanky-panky here: + * the string should look like "123456 (name)", + * we get it from /proc/XX/status as "Name: name\n...", + * to fit the 6 digits of PID and opening parenthesis, + * save 2 bytes at the start of the buffer. + * And don't forget to leave 2 bytes for the trailing + * parenthesis and the nil. Here is + * an example of what we're trying to achieve: + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | | | N| a| m| e| :|\t| i| n| i| t|\n| S|... + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | 0| 0| 0| 0| 0| 1| | (| i| n| i| t| )|\0|... + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ + if ((k = read(fd, buf+2, sizeof(buf)-4)) > 0) { + int bc; + + if ((p = strchr(buf+2, '\n')) == NULL) + p = buf+k; + p[0] = ')'; + p[1] = '\0'; + bc = sprintf(buf, "%06d ", pids.pids[i]); + buf[bc] = '('; + } + close(fd); + } + } + + if (k <= 0) { + /* hmm .. must be exiting */ + sprintf(buf, "%06d ", pids.pids[i]); + } + + ep->name = strdup(buf); + + __pmHashAdd(pids.pids[i], (void *)ep, &proc_pid->pidhash); + // fprintf(stderr, "## ADDED \"%s\" to hash table\n", buf); + } + else + ep = (proc_pid_entry_t *)node->data; + + /* mark pid as still existing */ + ep->flags |= PROC_PID_FLAG_VALID; + + /* refresh the indom pointer */ + indomp->it_set[i].i_inst = ep->id; + indomp->it_set[i].i_name = ep->name; + } + + /* + * harvest exited pids from the pid hash table + */ + for (i=0; i < proc_pid->pidhash.hsize; i++) { + for (prev=NULL, node=proc_pid->pidhash.hash[i]; node != NULL;) { + next = node->next; + ep = (proc_pid_entry_t *)node->data; + // fprintf(stderr, "CHECKING key=%d node=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p ep=" PRINTF_P_PFX "%p valid=%d\n", + // ep->id, node, prev, node->next, ep, ep->valid); + if (!(ep->flags & PROC_PID_FLAG_VALID)) { + // fprintf(stderr, "DELETED key=%d name=\"%s\"\n", ep->id, ep->name); + if (ep->name != NULL) + free(ep->name); + if (ep->stat_buf != NULL) + free(ep->stat_buf); + if (ep->status_buf != NULL) + free(ep->status_buf); + if (ep->statm_buf != NULL) + free(ep->statm_buf); + if (ep->maps_buf != NULL) + free(ep->maps_buf); + if (ep->schedstat_buf != NULL) + free(ep->schedstat_buf); + if (ep->io_buf != NULL) + free(ep->io_buf); + if (ep->wchan_buf != NULL) + free(ep->wchan_buf); + + if (prev == NULL) + proc_pid->pidhash.hash[i] = node->next; + else + prev->next = node->next; + free(ep); + free(node); + } + else { + prev = node; + } + if ((node = next) == NULL) + break; + } + } +} + +int +refresh_proc_pid(proc_pid_t *proc_pid, int threads, const char *cgroups) +{ + int sts; + + pids.count = 0; + pids.threads = threads; + + sts = (cgroups && cgroups[0] != '\0') ? + refresh_cgroup_pidlist(threads, cgroups) : + refresh_global_pidlist(threads); + if (sts < 0) + return sts; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, + "refresh_proc_pid: %d pids (threads=%d, cgroups=\"%s\")\n", + sts, threads, cgroups ? cgroups : ""); +#endif + + refresh_proc_pidlist(proc_pid); + return 0; +} + + +/* + * Open a proc file, taking into account that we may want thread info + * rather than process information. + * + * We make (ab)use of some obscure Linux procfs mechanisms here! + * Even though readdir(/proc) does not contain tasks, we can still open + * taskid directory files; on top of that, the tasks sub-directory in a + * task group has all (peer) tasks in that group, even for "children". + */ +static int +proc_open(const char *base, proc_pid_entry_t *ep) +{ + int fd; + char buf[128]; + + if (pids.threads) { + sprintf(buf, "%s/proc/%d/task/%d/%s", proc_statspath, ep->id, ep->id, base); + if ((fd = open(buf, O_RDONLY)) >= 0) + return fd; + /* fallback to /proc path if task path open fails */ + } + sprintf(buf, "%s/proc/%d/%s", proc_statspath, ep->id, base); + return open(buf, O_RDONLY); +} + +static DIR * +proc_opendir(const char *base, proc_pid_entry_t *ep) +{ + DIR *dir; + char buf[128]; + + if (pids.threads) { + sprintf(buf, "%s/proc/%d/task/%d/%s", proc_statspath, ep->id, ep->id, base); + if ((dir = opendir(buf)) != NULL) + return dir; + /* fallback to /proc path if task path opendir fails */ + } + sprintf(buf, "%s/proc/%d/%s", proc_statspath, ep->id, base); + return opendir(buf); +} + +/* + * fetch a proc//stat entry for pid + */ +proc_pid_entry_t * +fetch_proc_pid_stat(int id, proc_pid_t *proc_pid) +{ + int fd; + int sts = 0; + int n; + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + char buf[1024]; + + if (node == NULL) { +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) == (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) { + char ibuf[1024]; + fprintf(stderr, "fetch_proc_pid_stat: __pmHashSearch(%d, hash[%s]) -> NULL\n", id, pmInDomStr_r(proc_pid->indom->it_indom, ibuf, sizeof(ibuf))); + } +#endif + return NULL; + } + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_STAT_FETCHED)) { + if ((fd = proc_open("stat", ep)) < 0) { + sts = -oserror(); +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) == (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) { + char ibuf[1024]; + char ebuf[1024]; + fprintf(stderr, "fetch_proc_pid_stat: proc_open(\"stat\", ...) failed: id=%d, indom=%s, sts=%s\n", id, pmInDomStr_r(proc_pid->indom->it_indom, ibuf, sizeof(ibuf)), pmErrStr_r(sts, ebuf, sizeof(ebuf))); + } +#endif + } + else { + if ((n = read(fd, buf, sizeof(buf))) < 0) { + sts = -oserror(); +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) == (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) { + char ibuf[1024]; + char ebuf[1024]; + fprintf(stderr, "fetch_proc_pid_stat: read \"stat\" failed: id=%d, indom=%s, sts=%s\n", id, pmInDomStr_r(proc_pid->indom->it_indom, ibuf, sizeof(ibuf)), pmErrStr_r(sts, ebuf, sizeof(ebuf))); + } +#endif + } + else { + if (n == 0) { + /* eh? */ + sts = -1; +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) == (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) { + char ibuf[1024]; + fprintf(stderr, "fetch_proc_pid_stat: read \"stat\" EOF?: id=%d, indom=%s\n", id, pmInDomStr_r(proc_pid->indom->it_indom, ibuf, sizeof(ibuf))); + } +#endif + } + else { + if (ep->stat_buflen <= n) { + ep->stat_buflen = n; + ep->stat_buf = (char *)realloc(ep->stat_buf, n); + } + memcpy(ep->stat_buf, buf, n); + ep->stat_buf[n-1] = '\0'; + sts = 0; + } + } + } + if (fd >= 0) + close(fd); + ep->flags |= PROC_PID_FLAG_STAT_FETCHED; + } + + if (!(ep->flags & PROC_PID_FLAG_WCHAN_FETCHED)) { + if ((fd = proc_open("wchan", ep)) < 0) { + /* ignore failure here, backwards compat */ + ; + } + else { + if ((n = read(fd, buf, sizeof(buf)-1)) < 0) { + sts = -oserror(); +#if PCP_DEBUG + if ((pmDebug & (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) == (DBG_TRACE_LIBPMDA|DBG_TRACE_DESPERATE)) { + char ibuf[1024]; + char ebuf[1024]; + fprintf(stderr, "fetch_proc_pid_stat: read \"wchan\" failed: id=%d, indom=%s, sts=%s\n", id, pmInDomStr_r(proc_pid->indom->it_indom, ibuf, sizeof(ibuf)), pmErrStr_r(sts, ebuf, sizeof(ebuf))); + } +#endif + } + else { + if (n == 0) { + /* wchan is empty, nothing to add here */ + ; + } + else { + n++; /* no terminating null (from kernel) */ + if (ep->wchan_buflen <= n) { + ep->wchan_buflen = n; + ep->wchan_buf = (char *)realloc(ep->wchan_buf, n); + } + memcpy(ep->wchan_buf, buf, n-1); + ep->wchan_buf[n-1] = '\0'; + } + } + } + if (fd >= 0) + close(fd); + ep->flags |= PROC_PID_FLAG_WCHAN_FETCHED; + } + + if (sts < 0) + return NULL; + return ep; +} + +/* + * fetch a proc//status entry for pid + * Added by Mike Mason + */ +proc_pid_entry_t * +fetch_proc_pid_status(int id, proc_pid_t *proc_pid) +{ + int sts = 0; + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_STATUS_FETCHED)) { + int fd; + int n; + char buf[1024]; + char *curline; + + if ((fd = proc_open("status", ep)) < 0) + sts = -oserror(); + else if ((n = read(fd, buf, sizeof(buf))) < 0) + sts = -oserror(); + else { + if (n == 0) + sts = -1; + else { + if (ep->status_buflen < n) { + ep->status_buflen = n; + ep->status_buf = (char *)realloc(ep->status_buf, n); + } + + if (ep->status_buf == NULL) + sts = -1; + else { + memcpy(ep->status_buf, buf, n); + ep->status_buf[n-1] = '\0'; + } + } + } + + if (sts == 0) { + /* assign pointers to individual lines in buffer */ + curline = ep->status_buf; + + while (strncmp(curline, "Uid:", 4)) { + curline = index(curline, '\n') + 1; + } + + /* user & group IDs */ + ep->status_lines.uid = strsep(&curline, "\n"); + ep->status_lines.gid = strsep(&curline, "\n"); + + while (curline) { + if (strncmp(curline, "VmSize:", 7) == 0) { + /* memory info - these lines don't exist for kernel threads */ + ep->status_lines.vmsize = strsep(&curline, "\n"); + ep->status_lines.vmlck = strsep(&curline, "\n"); + if (strncmp(curline, "VmRSS:", 6) != 0) + curline = index(curline, '\n') + 1; // Have VmPin: ? + if (strncmp(curline, "VmRSS:", 6) != 0) + curline = index(curline, '\n') + 1; // Have VmHWM: ? + ep->status_lines.vmrss = strsep(&curline, "\n"); + ep->status_lines.vmdata = strsep(&curline, "\n"); + ep->status_lines.vmstk = strsep(&curline, "\n"); + ep->status_lines.vmexe = strsep(&curline, "\n"); + ep->status_lines.vmlib = strsep(&curline, "\n"); + curline = index(curline, '\n') + 1; // skip VmPTE + ep->status_lines.vmswap = strsep(&curline, "\n"); + ep->status_lines.threads = strsep(&curline, "\n"); + } else + if (strncmp(curline, "SigPnd:", 7) == 0) { + /* signal masks */ + ep->status_lines.sigpnd = strsep(&curline, "\n"); + ep->status_lines.sigblk = strsep(&curline, "\n"); + ep->status_lines.sigign = strsep(&curline, "\n"); + ep->status_lines.sigcgt = strsep(&curline, "\n"); + break; /* we're done */ + } else { + curline = index(curline, '\n') + 1; + } + } + } + if (fd >= 0) + close(fd); + ep->flags |= PROC_PID_FLAG_STATUS_FETCHED; + } + + return (sts < 0) ? NULL : ep; +} + +/* + * fetch a proc//statm entry for pid + */ +proc_pid_entry_t * +fetch_proc_pid_statm(int id, proc_pid_t *proc_pid) +{ + int sts = 0; + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_STATM_FETCHED)) { + char buf[1024]; + int fd, n; + + if ((fd = proc_open("statm", ep)) < 0) + sts = -oserror(); + else + if ((n = read(fd, buf, sizeof(buf))) < 0) + sts = -oserror(); + else { + if (n == 0) + /* eh? */ + sts = -1; + else { + if (ep->statm_buflen <= n) { + ep->statm_buflen = n; + ep->statm_buf = (char *)realloc(ep->statm_buf, n); + } + memcpy(ep->statm_buf, buf, n); + ep->statm_buf[n-1] = '\0'; + } + } + + if (fd >= 0) + close(fd); + ep->flags |= PROC_PID_FLAG_STATM_FETCHED; + } + + return (sts < 0) ? NULL : ep; +} + + +/* + * fetch a proc//maps entry for pid + * WARNING: This can be very large! Only ask for it if you really need it. + * Added by Mike Mason + */ +proc_pid_entry_t * +fetch_proc_pid_maps(int id, proc_pid_t *proc_pid) +{ + int sts = 0; + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + char *maps_bufptr = NULL; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_MAPS_FETCHED)) { + int fd; + + if ((fd = proc_open("maps", ep)) < 0) + sts = -oserror(); + else { + char buf[1024]; + int n, len = 0; + + while ((n = read(fd, buf, sizeof(buf))) > 0) { + len += n; + if (ep->maps_buflen <= len) { + ep->maps_buflen = len + 1; + ep->maps_buf = (char *)realloc(ep->maps_buf, ep->maps_buflen); + } + maps_bufptr = ep->maps_buf + len - n; + memcpy(maps_bufptr, buf, n); + } + ep->flags |= PROC_PID_FLAG_MAPS_FETCHED; + /* If there are no maps, make maps_buf point to a zero length string. */ + if (ep->maps_buflen == 0) { + ep->maps_buf = (char *)malloc(1); + ep->maps_buflen = 1; + } + ep->maps_buf[ep->maps_buflen - 1] = '\0'; + close(fd); + } + } + + return (sts < 0) ? NULL : ep; +} + +/* + * fetch a proc//schedstat entry for pid + */ +proc_pid_entry_t * +fetch_proc_pid_schedstat(int id, proc_pid_t *proc_pid) +{ + int sts = 0; + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_SCHEDSTAT_FETCHED)) { + int fd, n; + char buf[1024]; + + if ((fd = proc_open("schedstat", ep)) < 0) + sts = -oserror(); + else + if ((n = read(fd, buf, sizeof(buf))) < 0) + sts = -oserror(); + else { + if (n == 0) + /* eh? */ + sts = -1; + else { + if (ep->schedstat_buflen <= n) { + ep->schedstat_buflen = n; + ep->schedstat_buf = (char *)realloc(ep->schedstat_buf, n); + } + memcpy(ep->schedstat_buf, buf, n); + ep->schedstat_buf[n-1] = '\0'; + } + } + if (fd >= 0) { + close(fd); + } + ep->flags |= PROC_PID_FLAG_SCHEDSTAT_FETCHED; + } + + return (sts < 0) ? NULL : ep; +} + +/* + * fetch a proc//io entry for pid + * + * Depends on kernel built with CONFIG_TASK_IO_ACCOUNTING=y + * which means the following must also be set: + * CONFIG_TASKSTATS=y + * CONFIG_TASK_DELAY_ACCT=y + * CONFIG_TASK_XACCT=y + */ +proc_pid_entry_t * +fetch_proc_pid_io(int id, proc_pid_t *proc_pid) +{ + int sts = 0; + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_IO_FETCHED)) { + int fd, n; + char buf[1024]; + char *curline; + + if ((fd = proc_open("io", ep)) < 0) + sts = -oserror(); + else if ((n = read(fd, buf, sizeof(buf))) < 0) + sts = -oserror(); + else { + if (n == 0) + sts = -1; + else { + if (ep->io_buflen < n) { + ep->io_buflen = n; + ep->io_buf = (char *)realloc(ep->io_buf, n); + } + + if (ep->io_buf == NULL) + sts = -1; + else { + memcpy(ep->io_buf, buf, n); + ep->io_buf[n-1] = '\0'; + } + } + } + + if (sts == 0) { + /* assign pointers to individual lines in buffer */ + curline = ep->io_buf; + ep->io_lines.rchar = strsep(&curline, "\n"); + ep->io_lines.wchar = strsep(&curline, "\n"); + ep->io_lines.syscr = strsep(&curline, "\n"); + ep->io_lines.syscw = strsep(&curline, "\n"); + ep->io_lines.readb = strsep(&curline, "\n"); + ep->io_lines.writeb = strsep(&curline, "\n"); + ep->io_lines.cancel = strsep(&curline, "\n"); + ep->flags |= PROC_PID_FLAG_IO_FETCHED; + } + if (fd >= 0) + close(fd); + } + + return (sts < 0) ? NULL : ep; +} + +/* + * fetch a proc//fd entry for pid + */ +proc_pid_entry_t * +fetch_proc_pid_fd(int id, proc_pid_t *proc_pid) +{ + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_FD_FETCHED)) { + uint32_t de_count = 0; + DIR *dir = proc_opendir("fd", ep); + + if (dir == NULL) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, "failed to open fd path for pid %d\n", ep->id); +#endif + return NULL; + } + while (readdir(dir) != NULL) { + de_count++; + } + closedir(dir); + ep->fd_count = de_count - 2; /* subtract cwd and parent entries */ + ep->flags |= PROC_PID_FLAG_FD_FETCHED; + } + + return ep; +} + +/* + * From the kernel format for a single process cgroup set: + * 2:cpu:/ + * 1:cpuset:/ + * + * Produce the same one-line format string that "ps" uses: + * "cpu:/;cpuset:/" + */ +static void +proc_cgroup_reformat(char *buf, int len, char *fmt) +{ + char *target = fmt, *p, *s = NULL; + + *target = '\0'; + for (p = buf; p - buf < len; p++) { + if (*p == '\0') + break; + if (*p == ':' && !s) /* position "s" at start */ + s = p + 1; + if (*p != '\n' || !s) /* find end of this line */ + continue; + if (target != fmt) /* not the first cgroup? */ + strncat(target, ";", 2); + /* have a complete cgroup line now, copy it over */ + strncat(target, s, (p - s)); + target += (p - s); + s = NULL; /* reset it for new line */ + } +} + +/* + * fetch a proc//cgroup entry for pid + */ +proc_pid_entry_t * +fetch_proc_pid_cgroup(int id, proc_pid_t *proc_pid) +{ + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + int sts = 0; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_CGROUP_FETCHED)) { + char buf[1024]; + char fmt[1024]; + int n, fd; + + if ((fd = proc_open("cgroup", ep)) < 0) + sts = -oserror(); + else if ((n = read(fd, buf, sizeof(buf))) < 0) + sts = -oserror(); + else { + if (n == 0) { + setoserror(ENODATA); + sts = -1; + } + else { + /* reformat the buffer to match "ps" output format, then hash */ + proc_cgroup_reformat(&buf[0], n, &fmt[0]); + ep->cgroup_id = proc_strings_insert(fmt); + } + } + if (fd >= 0) + close(fd); + ep->flags |= PROC_PID_FLAG_CGROUP_FETCHED; + } + + return (sts < 0) ? NULL : ep; +} + +/* + * fetch a proc//attr/current entry for pid + */ +proc_pid_entry_t * +fetch_proc_pid_label(int id, proc_pid_t *proc_pid) +{ + __pmHashNode *node = __pmHashSearch(id, &proc_pid->pidhash); + proc_pid_entry_t *ep; + int sts = 0; + + if (node == NULL) + return NULL; + ep = (proc_pid_entry_t *)node->data; + + if (!(ep->flags & PROC_PID_FLAG_LABEL_FETCHED)) { + char buf[1024]; + int n, fd; + + if ((fd = proc_open("attr/current", ep)) < 0) + sts = -oserror(); + else if ((n = read(fd, buf, sizeof(buf))) < 0) + sts = -oserror(); + else { + if (n == 0) { + setoserror(ENODATA); + sts = -1; + } else { + /* buffer matches "ps" output format, direct hash */ + buf[sizeof(buf)-1] = '\0'; + ep->label_id = proc_strings_insert(buf); + } + } + if (fd >= 0) + close(fd); + ep->flags |= PROC_PID_FLAG_LABEL_FETCHED; + } + + return (sts < 0) ? NULL : ep; +} + +/* + * Extract the ith (space separated) field from a char buffer. + * The first field starts at zero. + * BEWARE: return copy is in a static buffer. + */ +char * +_pm_getfield(char *buf, int field) +{ + static int retbuflen = 0; + static char *retbuf = NULL; + char *p; + int i; + + if (buf == NULL) + return NULL; + + for (p=buf, i=0; i < field; i++) { + /* skip to the next space */ + for (; *p && !isspace((int)*p); p++) {;} + + /* skip to the next word */ + for (; *p && isspace((int)*p); p++) {;} + } + + /* return a null terminated copy of the field */ + for (i=0; ; i++) { + if (isspace((int)p[i]) || p[i] == '\0' || p[i] == '\n') + break; + } + + if (i >= retbuflen) { + retbuflen = i+4; + retbuf = (char *)realloc(retbuf, retbuflen); + } + memcpy(retbuf, p, i); + retbuf[i] = '\0'; + + return retbuf; +} diff --git a/src/pmdas/linux_proc/proc_pid.h b/src/pmdas/linux_proc/proc_pid.h new file mode 100644 index 0000000..8835157 --- /dev/null +++ b/src/pmdas/linux_proc/proc_pid.h @@ -0,0 +1,289 @@ +/* + * Linux /proc//... Clusters + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _PROC_PID_H +#define _PROC_PID_H + +/* + * /proc//stat metrics + */ +#define PROC_PID_STAT_PID 0 +#define PROC_PID_STAT_CMD 1 +#define PROC_PID_STAT_STATE 2 +#define PROC_PID_STAT_PPID 3 +#define PROC_PID_STAT_PGRP 4 +#define PROC_PID_STAT_SESSION 5 +#define PROC_PID_STAT_TTY 6 +#define PROC_PID_STAT_TTY_PGRP 7 +#define PROC_PID_STAT_FLAGS 8 +#define PROC_PID_STAT_MINFLT 9 +#define PROC_PID_STAT_CMIN_FLT 10 +#define PROC_PID_STAT_MAJ_FLT 11 +#define PROC_PID_STAT_CMAJ_FLT 12 +#define PROC_PID_STAT_UTIME 13 +#define PROC_PID_STAT_STIME 14 +#define PROC_PID_STAT_CUTIME 15 +#define PROC_PID_STAT_CSTIME 16 +#define PROC_PID_STAT_PRIORITY 17 +#define PROC_PID_STAT_NICE 18 +#define PROC_PID_STAT_REMOVED 19 +#define PROC_PID_STAT_IT_REAL_VALUE 20 +#define PROC_PID_STAT_START_TIME 21 +#define PROC_PID_STAT_VSIZE 22 +#define PROC_PID_STAT_RSS 23 +#define PROC_PID_STAT_RSS_RLIM 24 +#define PROC_PID_STAT_START_CODE 25 +#define PROC_PID_STAT_END_CODE 26 +#define PROC_PID_STAT_START_STACK 27 +#define PROC_PID_STAT_ESP 28 +#define PROC_PID_STAT_EIP 29 +#define PROC_PID_STAT_SIGNAL 30 +#define PROC_PID_STAT_BLOCKED 31 +#define PROC_PID_STAT_SIGIGNORE 32 +#define PROC_PID_STAT_SIGCATCH 33 +#define PROC_PID_STAT_WCHAN 34 +#define PROC_PID_STAT_NSWAP 35 +#define PROC_PID_STAT_CNSWAP 36 +#define PROC_PID_STAT_EXIT_SIGNAL 37 +#define PROC_PID_STAT_PROCESSOR 38 +#define PROC_PID_STAT_TTYNAME 39 +#define PROC_PID_STAT_WCHAN_SYMBOL 40 +#define PROC_PID_STAT_PSARGS 41 + +/* number of fields in proc_pid_stat_entry_t */ +#define NR_PROC_PID_STAT 42 + +/* + * metrics in /proc//status + * Added by Mike Mason + */ +#define PROC_PID_STATUS_UID 0 +#define PROC_PID_STATUS_EUID 1 +#define PROC_PID_STATUS_SUID 2 +#define PROC_PID_STATUS_FSUID 3 +#define PROC_PID_STATUS_GID 4 +#define PROC_PID_STATUS_EGID 5 +#define PROC_PID_STATUS_SGID 6 +#define PROC_PID_STATUS_FSGID 7 +#define PROC_PID_STATUS_UID_NM 8 +#define PROC_PID_STATUS_EUID_NM 9 +#define PROC_PID_STATUS_SUID_NM 10 +#define PROC_PID_STATUS_FSUID_NM 11 +#define PROC_PID_STATUS_GID_NM 12 +#define PROC_PID_STATUS_EGID_NM 13 +#define PROC_PID_STATUS_SGID_NM 14 +#define PROC_PID_STATUS_FSGID_NM 15 +#define PROC_PID_STATUS_SIGNAL 16 +#define PROC_PID_STATUS_BLOCKED 17 +#define PROC_PID_STATUS_SIGIGNORE 18 +#define PROC_PID_STATUS_SIGCATCH 19 +#define PROC_PID_STATUS_VMSIZE 20 +#define PROC_PID_STATUS_VMLOCK 21 +#define PROC_PID_STATUS_VMRSS 22 +#define PROC_PID_STATUS_VMDATA 23 +#define PROC_PID_STATUS_VMSTACK 24 +#define PROC_PID_STATUS_VMEXE 25 +#define PROC_PID_STATUS_VMLIB 26 +#define PROC_PID_STATUS_VMSWAP 27 +#define PROC_PID_STATUS_THREADS 28 + +/* number of metrics from /proc//status */ +#define NR_PROC_PID_STATUS 27 + +/* + * metrics in /proc//statm & /proc//maps + */ +#define PROC_PID_STATM_SIZE 0 +#define PROC_PID_STATM_RSS 1 +#define PROC_PID_STATM_SHARE 2 +#define PROC_PID_STATM_TEXTRS 3 +#define PROC_PID_STATM_LIBRS 4 +#define PROC_PID_STATM_DATRS 5 +#define PROC_PID_STATM_DIRTY 6 +#define PROC_PID_STATM_MAPS 7 + +/* number of fields in proc_pid_statm_entry_t */ +#define NR_PROC_PID_STATM 8 + +/* + * metrics in /proc//schedstat + */ +#define PROC_PID_SCHED_CPUTIME 0 +#define PROC_PID_SCHED_RUNDELAY 1 +#define PROC_PID_SCHED_PCOUNT 2 +#define NR_PROC_PID_SCHED 3 + +/* + * metrics in /proc//io + */ +#define PROC_PID_IO_RCHAR 0 +#define PROC_PID_IO_WCHAR 1 +#define PROC_PID_IO_SYSCR 2 +#define PROC_PID_IO_SYSCW 3 +#define PROC_PID_IO_READ_BYTES 4 +#define PROC_PID_IO_WRITE_BYTES 5 +#define PROC_PID_IO_CANCELLED_BYTES 6 + +/* + * metrics in /proc//fd + */ +#define PROC_PID_FD_COUNT 0 + + +/* + * metrics in /proc//cgroup + */ +#define PROC_PID_CGROUP 0 + +/* + * metrics in /proc//attr/current + */ +#define PROC_PID_LABEL 0 + +typedef struct { /* /proc//status */ + char *uid; + char *gid; + char *sigpnd; + char *sigblk; + char *sigign; + char *sigcgt; + char *vmsize; + char *vmlck; + char *vmrss; + char *vmdata; + char *vmstk; + char *vmexe; + char *vmlib; + char *vmswap; + char *threads; +} status_lines_t; + +typedef struct { /* /proc//io */ + char *rchar; + char *wchar; + char *syscr; + char *syscw; + char *readb; + char *writeb; + char *cancel; +} io_lines_t; + +enum { + PROC_PID_FLAG_VALID = 1<<0, + PROC_PID_FLAG_STAT_FETCHED = 1<<1, + PROC_PID_FLAG_STATM_FETCHED = 1<<2, + PROC_PID_FLAG_MAPS_FETCHED = 1<<3, + PROC_PID_FLAG_STATUS_FETCHED = 1<<4, + PROC_PID_FLAG_SCHEDSTAT_FETCHED = 1<<5, + PROC_PID_FLAG_IO_FETCHED = 1<<6, + PROC_PID_FLAG_WCHAN_FETCHED = 1<<7, + PROC_PID_FLAG_FD_FETCHED = 1<<8, + PROC_PID_FLAG_CGROUP_FETCHED = 1<<9, + PROC_PID_FLAG_LABEL_FETCHED = 1<<10, +}; + +typedef struct { + int id; /* pid, hash key and internal instance id */ + int flags; /* combinations of PROC_PID_FLAG_* values */ + char *name; /* external instance name ( cmdline) */ + + /* /proc//stat cluster */ + int stat_buflen; + char *stat_buf; + + /* /proc//statm and /proc//maps cluster */ + int statm_buflen; + char *statm_buf; + int maps_buflen; + char *maps_buf; + + /* /proc//status cluster */ + int status_buflen; + char *status_buf; + status_lines_t status_lines; + + /* /proc//schedstat cluster */ + int schedstat_buflen; + char *schedstat_buf; + + /* /proc//io cluster */ + int io_buflen; + char *io_buf; + io_lines_t io_lines; + + /* /proc//wchan cluster */ + int wchan_buflen; + char *wchan_buf; + + /* /proc//fd cluster */ + int fd_buflen; + uint32_t fd_count; + char *fd_buf; + + /* /proc//cgroup cluster */ + int cgroup_id; + + /* /proc//attr/current cluster */ + int label_id; +} proc_pid_entry_t; + +typedef struct { + __pmHashCtl pidhash; /* hash table for current pids */ + pmdaIndom *indom; /* instance domain table */ +} proc_pid_t; + +typedef struct { + int count; /* number of processes in the list */ + int size; /* size of the buffer (pids) allocated */ + int *pids; /* array of process identifiers */ + int threads; /* /proc/PID/{xxx,task/PID/xxx} flag */ +} proc_pid_list_t; + +/* refresh the proc indom, reset all "fetched" flags */ +extern int refresh_proc_pid(proc_pid_t *, int, const char *); + +/* fetch a proc//stat entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_stat(int, proc_pid_t *); + +/* fetch a proc//statm entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_statm(int, proc_pid_t *); + +/* fetch a proc//status entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_status(int, proc_pid_t *); + +/* fetch a proc//maps entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_maps(int, proc_pid_t *); + +/* fetch a proc//schedstat entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_schedstat(int, proc_pid_t *); + +/* fetch a proc//io entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_io(int, proc_pid_t *); + +/* fetch a proc//fd entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_fd(int, proc_pid_t *); + +/* fetch a proc//cgroup entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_cgroup(int, proc_pid_t *); + +/* fetch a proc//attr/current entry for pid */ +extern proc_pid_entry_t *fetch_proc_pid_label(int, proc_pid_t *); + +/* extract the ith space separated field from a buffer */ +extern char *_pm_getfield(char *, int); + +#endif /* _PROC_PID_H */ diff --git a/src/pmdas/linux_proc/proc_runq.c b/src/pmdas/linux_proc/proc_runq.c new file mode 100644 index 0000000..07b68dc --- /dev/null +++ b/src/pmdas/linux_proc/proc_runq.c @@ -0,0 +1,123 @@ +/* + * Linux /proc/runq metrics cluster + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include +#include +#include +#include "proc_pid.h" +#include "proc_runq.h" + +int +refresh_proc_runq(proc_runq_t *proc_runq) +{ + int sz; + int fd; + char *p; + int sname; + DIR *dir; + struct dirent *d; + char fullpath[MAXPATHLEN]; + char buf[4096]; + + memset(proc_runq, 0, sizeof(proc_runq_t)); + if ((dir = opendir("/proc")) == NULL) + return -oserror(); + + while((d = readdir(dir)) != NULL) { + if (!isdigit((int)d->d_name[0])) + continue; + sprintf(fullpath, "/proc/%s/stat", d->d_name); + if ((fd = open(fullpath, O_RDONLY)) < 0) + continue; + sz = read(fd, buf, sizeof(buf)); + close(fd); + buf[sizeof(buf)-1] = '\0'; + + /* + * defunct (state name is 'Z') + */ + if (sz <= 0 || (p = _pm_getfield(buf, PROC_PID_STAT_STATE)) == NULL) { + proc_runq->unknown++; + continue; + } + if ((sname = *p) == 'Z') { + proc_runq->defunct++; + continue; + } + + /* + * kernel process (not defunct and virtual size is zero) + */ + if ((p = _pm_getfield(buf, PROC_PID_STAT_VSIZE)) == NULL) { + proc_runq->unknown++; + continue; + } + if (strcmp(p, "0") == 0) { + proc_runq->kernel++; + continue; + } + + /* + * swapped (resident set size is zero) + */ + if ((p = _pm_getfield(buf, PROC_PID_STAT_RSS)) == NULL) { + proc_runq->unknown++; + continue; + } + if (strcmp(p, "0") == 0) { + proc_runq->swapped++; + continue; + } + + /* + * All other states + */ + switch (sname) { + case 'R': + proc_runq->runnable++; + break; + case 'S': + proc_runq->sleeping++; + break; + case 'T': + proc_runq->stopped++; + break; + case 'D': + proc_runq->blocked++; + break; + /* case 'Z': + break; -- already counted above */ + default: + fprintf(stderr, "UNKNOWN %c : %s\n", sname, buf); + proc_runq->unknown++; + break; + } + } + closedir(dir); + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_LIBPMDA) { + fprintf(stderr, "refresh_runq: runnable=%d sleeping=%d stopped=%d blocked=%d unknown=%d\n", + proc_runq->runnable, proc_runq->sleeping, proc_runq->stopped, + proc_runq->blocked, proc_runq->unknown); + } +#endif + + return 0; +} diff --git a/src/pmdas/linux_proc/proc_runq.h b/src/pmdas/linux_proc/proc_runq.h new file mode 100644 index 0000000..9739208 --- /dev/null +++ b/src/pmdas/linux_proc/proc_runq.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PROC_RUNQ_H +#define _PROC_RUNQ_H + +typedef struct { + int runnable; + int blocked; + int sleeping; + int stopped; + int swapped; + int kernel; + int defunct; + int unknown; +} proc_runq_t; + +extern int refresh_proc_runq(proc_runq_t *); + +#endif /* _PROC_RUNQ_H */ diff --git a/src/pmdas/linux_proc/root b/src/pmdas/linux_proc/root new file mode 100644 index 0000000..5f26a89 --- /dev/null +++ b/src/pmdas/linux_proc/root @@ -0,0 +1,6 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include +#include "root_xfs" diff --git a/src/pmdas/linux_proc/root_proc b/src/pmdas/linux_proc/root_proc new file mode 100644 index 0000000..91b8654 --- /dev/null +++ b/src/pmdas/linux_proc/root_proc @@ -0,0 +1,181 @@ +/* + * Metrics for the Linux proc PMDA + * + * Note: + * names and pmids migrated from the Linux PMDA, with the domain + * number changed from LINUX (60) to 3 (3) + */ + +#ifndef PROC +#define PROC 3 +#endif + +root { + cgroup + proc +} + +cgroup { + subsys + mounts + groups PROC:*:* +} + +cgroup.subsys { + hierarchy PROC:37:0 + count PROC:37:1 +} + +cgroup.mounts { + subsys PROC:38:0 + count PROC:38:1 +} + +proc { + nprocs PROC:8:99 + psinfo + memory + runq + id + io + schedstat + fd + control +} + +proc.psinfo { + pid PROC:8:0 + cmd PROC:8:1 + sname PROC:8:2 + ppid PROC:8:3 + pgrp PROC:8:4 + session PROC:8:5 + tty PROC:8:6 + tty_pgrp PROC:8:7 + flags PROC:8:8 + minflt PROC:8:9 + cmin_flt PROC:8:10 + maj_flt PROC:8:11 + cmaj_flt PROC:8:12 + utime PROC:8:13 + stime PROC:8:14 + cutime PROC:8:15 + cstime PROC:8:16 + priority PROC:8:17 + nice PROC:8:18 + /* not valid in 2.2.1 PROC:8:19 */ + it_real_value PROC:8:20 + start_time PROC:8:21 + vsize PROC:8:22 + rss PROC:8:23 + rss_rlim PROC:8:24 + start_code PROC:8:25 + end_code PROC:8:26 + start_stack PROC:8:27 + esp PROC:8:28 + eip PROC:8:29 + signal PROC:8:30 + blocked PROC:8:31 + sigignore PROC:8:32 + sigcatch PROC:8:33 + wchan PROC:8:34 + nswap PROC:8:35 + cnswap PROC:8:36 + exit_signal PROC:8:37 + processor PROC:8:38 + ttyname PROC:8:39 + wchan_s PROC:8:40 + psargs PROC:8:41 + signal_s PROC:24:16 + blocked_s PROC:24:17 + sigignore_s PROC:24:18 + sigcatch_s PROC:24:19 + threads PROC:24:28 + cgroups PROC:11:0 + labels PROC:12:0 +} + +proc.id { + uid PROC:24:0 + euid PROC:24:1 + suid PROC:24:2 + fsuid PROC:24:3 + gid PROC:24:4 + egid PROC:24:5 + sgid PROC:24:6 + fsgid PROC:24:7 + uid_nm PROC:24:8 + euid_nm PROC:24:9 + suid_nm PROC:24:10 + fsuid_nm PROC:24:11 + gid_nm PROC:24:12 + egid_nm PROC:24:13 + sgid_nm PROC:24:14 + fsgid_nm PROC:24:15 +} + +proc.memory { + size PROC:9:0 + rss PROC:9:1 + share PROC:9:2 + textrss PROC:9:3 + librss PROC:9:4 + datrss PROC:9:5 + dirty PROC:9:6 + maps PROC:9:7 + vmsize PROC:24:20 + vmlock PROC:24:21 + vmrss PROC:24:22 + vmdata PROC:24:23 + vmstack PROC:24:24 + vmexe PROC:24:25 + vmlib PROC:24:26 + vmswap PROC:24:27 +} + +proc.runq { + runnable PROC:13:0 + blocked PROC:13:1 + sleeping PROC:13:2 + stopped PROC:13:3 + swapped PROC:13:4 + defunct PROC:13:5 + unknown PROC:13:6 + kernel PROC:13:7 +} + +proc.io { + rchar PROC:32:0 + wchar PROC:32:1 + syscr PROC:32:2 + syscw PROC:32:3 + read_bytes PROC:32:4 + write_bytes PROC:32:5 + cancelled_write_bytes PROC:32:6 +} + +proc.schedstat { + cpu_time PROC:31:0 + run_delay PROC:31:1 + pcount PROC:31:2 +} + +proc.fd { + count PROC:51:0 +} + +proc.control { + all + perclient +} + +proc.control.all { + threads PROC:10:1 +} + +proc.control.perclient { + threads PROC:10:2 + cgroups PROC:10:3 +} + +#undef PROC diff --git a/src/pmdas/linux_xfs/GNUmakefile b/src/pmdas/linux_xfs/GNUmakefile new file mode 100644 index 0000000..211f651 --- /dev/null +++ b/src/pmdas/linux_xfs/GNUmakefile @@ -0,0 +1,76 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2000,2003,2004,2008 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = xfs +DOMAIN = XFS +CMDTARGET = pmda$(IAM) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +PMDAINIT = $(IAM)_init +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "xfs 11 pipe binary $(PMDADIR)/$(CMDTARGET) -d 11" + +CFILES = proc_fs_xfs.c filesys.c pmda.c +HFILES = proc_fs_xfs.h filesys.h clusters.h indom.h + +SCRIPTS = Install Remove +VERSION_SCRIPT = exports +HELPTARGETS = help.dir help.pag +LSRCFILES = help root root_xfs linux_xfs_migrate.conf $(SCRIPTS) +LDIRT = $(HELPTARGETS) domain.h $(VERSION_SCRIPT) + +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(LIBTARGET) $(CMDTARGET) $(HELPTARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help help.dir help.pag root root_xfs $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 root_xfs $(PCP_VAR_DIR)/pmns/root_xfs + $(INSTALL) -m 644 linux_xfs_migrate.conf $(PCP_VAR_DIR)/config/pmlogrewrite/linux_xfs_migrate.conf +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +$(HELPTARGETS) : help + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_xfs -v 2 -o help < help + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +pmda.o: domain.h +pmda.o proc_fs_xfs.o: proc_fs_xfs.h +filesys.o pmda.o: filesys.h +pmda.o: $(VERSION_SCRIPT) diff --git a/src/pmdas/linux_xfs/Install b/src/pmdas/linux_xfs/Install new file mode 100755 index 0000000..4f68af7 --- /dev/null +++ b/src/pmdas/linux_xfs/Install @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (c) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Linux XFS PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=xfs +pmda_interface=3 +daemon_opt=true +pipe_opt=true +pmns_source=root_xfs + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/linux_xfs/Remove b/src/pmdas/linux_xfs/Remove new file mode 100755 index 0000000..1210f45 --- /dev/null +++ b/src/pmdas/linux_xfs/Remove @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright (c) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Linux XFS PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh +iam=xfs +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/linux_xfs/clusters.h b/src/pmdas/linux_xfs/clusters.h new file mode 100644 index 0000000..a984343 --- /dev/null +++ b/src/pmdas/linux_xfs/clusters.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2005,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _CLUSTERS_H +#define _CLUSTERS_H + +/* + * PMID cluster values ... to manage the PMID migration after the + * linux -> linux + xfs PMDAs split, these need to match the enum + * assigned values for CLUSTER_* from the original Linux PMDA. + */ +#define CLUSTER_XFS 16 /* /proc/fs/xfs/stat */ +#define CLUSTER_XFSBUF 17 /* /proc/fs/pagebuf/stat */ +#define CLUSTER_QUOTA 30 /* quotactl() */ + +#define MIN_CLUSTER 16 /* first cluster number we use here */ +#define NUM_CLUSTERS 31 /* one more than highest cluster number used */ + +#endif /* _CLUSTERS_H */ diff --git a/src/pmdas/linux_xfs/filesys.c b/src/pmdas/linux_xfs/filesys.c new file mode 100644 index 0000000..34f8b66 --- /dev/null +++ b/src/pmdas/linux_xfs/filesys.c @@ -0,0 +1,183 @@ +/* + * XFS Filesystems Cluster + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2004,2007 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "filesys.h" +#include "proc_fs_xfs.h" + +static void +refresh_filesys_projects(pmInDom qindom, filesys_t *fs) +{ + char buffer[MAXPATHLEN]; + project_t *qp; + fs_quota_stat_t s; + fs_disk_quota_t d; + size_t idsz, devsz; + FILE *projects; + char *p, *idend; + uint32_t prid; + int qcmd, sts; + + qcmd = QCMD(Q_XGETQSTAT, XQM_PRJQUOTA); + if (quotactl(qcmd, fs->device, 0, (void*)&s) < 0) + return; + + if (s.qs_flags & XFS_QUOTA_PDQ_ENFD) + fs->flags |= FSF_QUOT_PROJ_ENF; + if (s.qs_flags & XFS_QUOTA_PDQ_ACCT) + fs->flags |= FSF_QUOT_PROJ_ACC; + else + return; + + if ((projects = xfs_statsfile("/etc/projects", "r")) == NULL) + return; + + qcmd = QCMD(Q_XQUOTASYNC, XQM_PRJQUOTA); + quotactl(qcmd, fs->device, 0, NULL); + + while (fgets(buffer, sizeof(buffer), projects)) { + if (buffer[0] == '#') + continue; + + prid = strtol(buffer, &idend, 10); + idsz = idend - buffer; + qcmd = QCMD(Q_XGETQUOTA, XQM_PRJQUOTA); + if (!idsz || quotactl(qcmd, fs->device, prid, (void *)&d) < 0) + continue; + + devsz = strlen(fs->device); + p = malloc(idsz+devsz+2); + if (!p) + continue; + memcpy(p, buffer, idsz); + p[idsz] = ':'; + memcpy(&p[idsz+1], fs->device, devsz+1); + + qp = NULL; + sts = pmdaCacheLookupName(qindom, p, NULL, (void **)&qp); + if (sts == PMDA_CACHE_ACTIVE) /* repeated line in /etc/projects? */ + goto next; + if (sts != PMDA_CACHE_INACTIVE) { + qp = (project_t *)malloc(sizeof(project_t)); + if (!qp) + goto next; + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, "refresh_filesys_projects: add \"%s\"\n", p); + } + qp->space_hard = d.d_blk_hardlimit; + qp->space_soft = d.d_blk_softlimit; + qp->space_used = d.d_bcount; + qp->space_time_left = d.d_btimer; + qp->files_hard = d.d_ino_hardlimit; + qp->files_soft = d.d_ino_softlimit; + qp->files_used = d.d_icount; + qp->files_time_left = d.d_itimer; + pmdaCacheStore(qindom, PMDA_CACHE_ADD, p, (void *)qp); +next: + free(p); + } + fclose(projects); +} + +char * +scan_filesys_options(const char *options, const char *option) +{ + static char buffer[128]; + char *s; + + strncpy(buffer, options, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + + s = strtok(buffer, ","); + while (s) { + if (strcmp(s, option) == 0) + return s; + s = strtok(NULL, ","); + } + return NULL; +} + +int +refresh_filesys(pmInDom filesys_indom, pmInDom quota_indom) +{ + char buf[MAXPATHLEN]; + char realdevice[MAXPATHLEN]; + filesys_t *fs; + pmInDom indom = filesys_indom; + FILE *fp; + char *path, *device, *type, *options; + int sts; + + pmdaCacheOp(quota_indom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(filesys_indom, PMDA_CACHE_INACTIVE); + + if ((fp = xfs_statsfile("/proc/mounts", "r")) == NULL) + return -oserror(); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((device = strtok(buf, " ")) == 0) + continue; + + path = strtok(NULL, " "); + type = strtok(NULL, " "); + options = strtok(NULL, " "); + if (strcmp(type, "xfs") != 0) + continue; + if (strncmp(device, "/dev", 4) != 0) + continue; + if (realpath(device, realdevice) != NULL) + device = realdevice; + + sts = pmdaCacheLookupName(indom, device, NULL, (void **)&fs); + if (sts == PMDA_CACHE_ACTIVE) /* repeated line in /proc/mounts? */ + continue; + if (sts == PMDA_CACHE_INACTIVE) { /* re-activate an old mount */ + pmdaCacheStore(indom, PMDA_CACHE_ADD, device, fs); + if (strcmp(path, fs->path) != 0) { /* old device, new path */ + free(fs->path); + fs->path = strdup(path); + } + if (strcmp(options, fs->options) != 0) { /* old device, new opts */ + free(fs->options); + fs->options = strdup(options); + } + } + else { /* new mount */ + if ((fs = malloc(sizeof(filesys_t))) == NULL) + continue; + fs->device = strdup(device); + fs->path = strdup(path); + fs->options = strdup(options); + if (pmDebug & DBG_TRACE_LIBPMDA) + fprintf(stderr, "refresh_filesys: add \"%s\" \"%s\"\n", + fs->path, device); + pmdaCacheStore(indom, PMDA_CACHE_ADD, device, fs); + } + fs->flags = 0; + refresh_filesys_projects(quota_indom, fs); + } + + /* + * success + * Note: we do not call statfs() here since only some instances + * may be requested (rather, we do it in xfs_fetch, see pmda.c). + */ + fclose(fp); + return 0; +} diff --git a/src/pmdas/linux_xfs/filesys.h b/src/pmdas/linux_xfs/filesys.h new file mode 100644 index 0000000..d1a2ac4 --- /dev/null +++ b/src/pmdas/linux_xfs/filesys.h @@ -0,0 +1,108 @@ +/* + * XFS Filesystem Cluster + * + * Copyright (c) 2013 Red Hat. + * Copyright (c) 2004,2007 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include + +#define XQM_CMD(x) (('X'<<8)+(x)) /* note: forms first QCMD argument */ +#define XQM_COMMAND(x) (((x) & (0xff<<8)) == ('X'<<8)) /* test if for XFS */ +#define XQM_PRJQUOTA 2 +#define Q_XGETQUOTA XQM_CMD(3) /* get disk limits and usage */ +#define Q_XGETQSTAT XQM_CMD(5) /* get quota subsystem status */ +#define Q_XQUOTASYNC XQM_CMD(7) /* delalloc flush, updates dquots */ + +#define XFS_QUOTA_PDQ_ACCT (1<<4) /* project quota accounting */ +#define XFS_QUOTA_PDQ_ENFD (1<<5) /* project quota limits enforcement */ + +#define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */ + +/* + * Some basic information about 'quota files'. + */ +typedef struct fs_qfilestat { + uint64_t qfs_ino; /* inode number */ + uint64_t qfs_nblks; /* number of BBs 512-byte-blks */ + uint32_t qfs_nextents; /* number of extents */ +} fs_qfilestat_t; + +typedef struct fs_quota_stat { + char qs_version; /* version number for future changes */ + uint16_t qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */ + char qs_pad; /* unused */ + fs_qfilestat_t qs_uquota; /* user quota storage information */ + fs_qfilestat_t qs_gquota; /* group quota storage information */ + uint32_t qs_incoredqs; /* number of dquots incore */ + int32_t qs_btimelimit; /* limit for blks timer */ + int32_t qs_itimelimit; /* limit for inodes timer */ + int32_t qs_rtbtimelimit;/* limit for rt blks timer */ + uint16_t qs_bwarnlimit; /* limit for num warnings */ + uint16_t qs_iwarnlimit; /* limit for num warnings */ +} fs_quota_stat_t; + +#define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */ +typedef struct fs_disk_quota { + char d_version; /* version of this structure */ + char d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */ + uint16_t d_fieldmask; /* field specifier */ + uint32_t d_id; /* user, project, or group ID */ + uint64_t d_blk_hardlimit;/* absolute limit on disk blks */ + uint64_t d_blk_softlimit;/* preferred limit on disk blks */ + uint64_t d_ino_hardlimit;/* maximum # allocated inodes */ + uint64_t d_ino_softlimit;/* preferred inode limit */ + uint64_t d_bcount; /* # disk blocks owned by the user */ + uint64_t d_icount; /* # inodes owned by the user */ + int32_t d_itimer; /* zero if within inode limits */ + int32_t d_btimer; /* similar to above; for disk blocks */ + uint16_t d_iwarns; /* # warnings issued wrt num inodes */ + uint16_t d_bwarns; /* # warnings issued wrt disk blocks */ + int32_t d_padding2; /* padding2 - for future use */ + uint64_t d_rtb_hardlimit;/* absolute limit on realtime blks */ + uint64_t d_rtb_softlimit;/* preferred limit on RT disk blks */ + uint64_t d_rtbcount; /* # realtime blocks owned */ + int32_t d_rtbtimer; /* similar to above; for RT disk blks */ + uint16_t d_rtbwarns; /* # warnings issued wrt RT disk blks */ + int16_t d_padding3; /* padding3 - for future use */ + char d_padding4[8]; /* yet more padding */ +} fs_disk_quota_t; + +typedef struct project { + int32_t space_time_left; /* seconds */ + int32_t files_time_left; /* seconds */ + uint64_t space_hard; /* blocks */ + uint64_t space_soft; /* blocks */ + uint64_t space_used; /* blocks */ + uint64_t files_hard; + uint64_t files_soft; + uint64_t files_used; +} project_t; + +/* Values for flags in filesys_t */ +#define FSF_FETCHED (1U << 0) +#define FSF_QUOT_PROJ_ACC (1U << 1) +#define FSF_QUOT_PROJ_ENF (1U << 2) + +typedef struct filesys { + int id; + unsigned int flags; + char *device; + char *path; + char *options; + struct statfs stats; +} filesys_t; + +extern int refresh_filesys(pmInDom, pmInDom); +extern char *scan_filesys_options(const char *, const char *); diff --git a/src/pmdas/linux_xfs/help b/src/pmdas/linux_xfs/help new file mode 100644 index 0000000..852165c --- /dev/null +++ b/src/pmdas/linux_xfs/help @@ -0,0 +1,469 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2000,2004-2008 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Linux XFS PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ xfs.allocs.alloc_extent XFS extents allocated +Number of file system extents allocated over all XFS filesystems +@ xfs.allocs.alloc_block XFS blocks allocated +Number of file system blocks allocated over all XFS filesystems +@ xfs.allocs.free_extent XFS extents freed +Number of file system extents freed over all XFS filesystems +@ xfs.allocs.free_block XFS blocks freed +Number of file system blocks freed over all XFS filesystems + +@ xfs.alloc_btree.lookup lookups in XFS alloc btrees +Number of lookup operations in XFS filesystem allocation btrees +@ xfs.alloc_btree.compare compares in XFS alloc btrees +Number of compares in XFS filesystem allocation btree lookups +@ xfs.alloc_btree.insrec insertions in XFS alloc btrees +Number of extent records inserted into XFS filesystem allocation btrees +@ xfs.alloc_btree.delrec deletions in XFS alloc btrees +Number of extent records deleted from XFS filesystem allocation btrees + +@ xfs.block_map.read_ops block map read ops in XFS +Number of block map for read operations performed on XFS files +@ xfs.block_map.write_ops block map write ops in XFS +Number of block map for write operations performed on XFS files +@ xfs.block_map.unmap block unmap ops in XFS +Number of block unmap (delete) operations performed on XFS files +@ xfs.block_map.add_exlist extent list add ops in XFS +Number of extent list insertion operations for XFS files +@ xfs.block_map.del_exlist extent list delete ops in XFS +Number of extent list deletion operations for XFS files +@ xfs.block_map.look_exlist extent list lookup ops in XFS +Number of extent list lookup operations for XFS files +@ xfs.block_map.cmp_exlist extent list compare ops in XFS +Number of extent list comparisons in XFS extent list lookups + +@ xfs.bmap_btree.lookup block map btree lookup ops in XFS +Number of block map btree lookup operations on XFS files +@ xfs.bmap_btree.compare block map btree compare ops in XFS +Number of block map btree compare operations in XFS block map lookups +@ xfs.bmap_btree.insrec block map btree insert ops in XFS +Number of block map btree records inserted for XFS files +@ xfs.bmap_btree.delrec block map btree delete ops in XFS +Number of block map btree records deleted for XFS files + +@ xfs.dir_ops.lookup number of file name directory lookups +This is a count of the number of file name directory lookups in XFS +filesystems. It counts only those lookups which miss in the operating +system's directory name lookup cache and must search the real directory +structure for the name in question. The count is incremented once for +each level of a pathname search that results in a directory lookup. + +@ xfs.dir_ops.create number of directory entry creation operations +This is the number of times a new directory entry was created in XFS +filesystems. Each time that a new file, directory, link, symbolic link, +or special file is created in the directory hierarchy the count is +incremented. + +@ xfs.dir_ops.remove number of directory entry remove operations +This is the number of times an existing directory entry was removed in +XFS filesystems. Each time that a file, directory, link, symbolic link, +or special file is removed from the directory hierarchy the count is +incremented. + +@ xfs.dir_ops.getdents number of times the getdents operation is performed +This is the number of times the XFS directory getdents operation was +performed. The getdents operation is used by programs to read the +contents of directories in a file system independent fashion. This +count corresponds exactly to the number of times the getdents(2) system +call was successfully used on an XFS directory. + +@ xfs.transactions.sync number of synchronous meta-data transactions performed +This is the number of meta-data transactions which waited to be +committed to the on-disk log before allowing the process performing the +transaction to continue. These transactions are slower and more +expensive than asynchronous transactions, because they force the in +memory log buffers to be forced to disk more often and they wait for +the completion of the log buffer writes. + +@ xfs.transactions.async number of asynchronous meta-data transactions performed +This is the number of meta-data transactions which did not wait to be +committed to the on-disk log before allowing the process performing the +transaction to continue. These transactions are faster and more +efficient than synchronous transactions, because they commit their data +to the in memory log buffers without forcing those buffers to be +written to disk. This allows multiple asynchronous transactions to be +committed to disk in a single log buffer write. Most transactions used +in XFS file systems are asynchronous. + +@ xfs.transactions.empty number of meta-data transactions which committed without changing anything +This is the number of meta-data transactions which did not actually +change anything. These are transactions which were started for some +purpose, but in the end it turned out that no change was necessary. + +@ xfs.inode_ops.ig_attempts number of in memory inode lookup operations +This is the number of times the operating system looked for an XFS +inode in the inode cache. Whether the inode was found in the cache or +needed to be read in from the disk is not indicated here, but this can +be computed from the ig_found and ig_missed counts. + +@ xfs.inode_ops.ig_found number of successful in memory inode lookup operations +This is the number of times the operating system looked for an XFS +inode in the inode cache and found it. The closer this count is to the +ig_attempts count the better the inode cache is performing. + +@ xfs.inode_ops.ig_frecycle number of just missed in memory inode lookup operations +This is the number of times the operating system looked for an XFS +inode in the inode cache and saw that it was there but was unable to +use the in memory inode because it was being recycled by another +process. + +@ xfs.inode_ops.ig_missed number of failed in memory inode lookup operations +This is the number of times the operating system looked for an XFS +inode in the inode cache and the inode was not there. The further this +count is from the ig_attempts count the better. + +@ xfs.inode_ops.ig_dup number of inode cache insertions that fail because the inode is there +This is the number of times the operating system looked for an XFS +inode in the inode cache and found that it was not there but upon +attempting to add the inode to the cache found that another process had +already inserted it. + +@ xfs.inode_ops.ig_reclaims number of in memory inode recycle operations +This is the number of times the operating system recycled an XFS inode +from the inode cache in order to use the memory for that inode for +another purpose. Inodes are recycled in order to keep the inode cache +from growing without bound. If the reclaim rate is high it may be +beneficial to raise the vnode_free_ratio kernel tunable variable to +increase the size of inode cache. + +@ xfs.inode_ops.ig_attrchg number of inode attribute change operations +This is the number of times the operating system explicitly changed the +attributes of an XFS inode. For example, this could be to change the +inode's owner, the inode's size, or the inode's timestamps. + +@ xfs.log.writes number of buffer writes going to the disk from the log +This variable counts the number of log buffer writes going to the +physical log partitions of all XFS filesystems. Log data traffic is +proportional to the level of meta-data updating. Log buffer writes get +generated when they fill up or external syncs occur. + +@ xfs.log.blocks write throughput to the physical XFS log +This variable counts the number of Kbytes of information being written +to the physical log partitions of all XFS filesystems. Log data traffic +is proportional to the level of meta-data updating. The rate with which +log data gets written depends on the size of internal log buffers and +disk write speed. Therefore, filesystems with very high meta-data +updating may need to stripe the log partition or put the log partition +on a separate drive. + +@ xfs.log.write_ratio ratio of count of XFS log blocks written to log writes +The ratio of log blocks written to log writes. If block count isn't a +"reasonable" multiple of writes, then many small log writes are being +performed - this is suboptimal. Perfection is 64. Fine-grain control +can be obtained when this metric is used in conjuntion with pmstore(1) +and the xfs.control.reset metric. + +@ xfs.log.noiclogs count of failures for immediate get of buffered/internal +This variable keeps track of times when a logged transaction can not +get any log buffer space. When this occurs, all of the internal log +buffers are busy flushing their data to the physical on-disk log. + +@ xfs.log.force value from xs_log_force field of struct xfsstats +The number of times the in-core log is forced to disk. It is +equivalent to the number of successful calls to the function +xfs_log_force(). + +@ xfs.log.force_sleep value from xs_log_force_sleep field of struct xfsstats +This metric is exported from the xs_log_force_sleep field of struct xfsstats + +@ xfs.log_tail.try_logspace value from xs_try_logspace field of struct xfsstats +This metric is exported from the xs_try_logspace field of struct xfsstats + +@ xfs.log_tail.sleep_logspace value from xs_sleep_logspace field of struct xfsstats +This metric is exported from the xs_sleep_logspace field of struct xfsstats + +@ xfs.log_tail.push_ail.pushes number of times the AIL tail is moved forward +The number of times the tail of the AIL is moved forward. It is +equivalent to the number of successful calls to the function +xfs_trans_push_ail(). + +@ xfs.log_tail.push_ail.success value from xs_push_ail_success field of struct xfsstats +@ xfs.log_tail.push_ail.pushbuf value from xs_push_ail_pushbuf field of struct xfsstats +@ xfs.log_tail.push_ail.pinned value from xs_push_ail_pinned field of struct xfsstats +@ xfs.log_tail.push_ail.locked value from xs_push_ail_locked field of struct xfsstats +@ xfs.log_tail.push_ail.flushing value from xs_push_ail_flushing field of struct xfsstats +@ xfs.log_tail.push_ail.restarts value from xs_push_ail_restarts field of struct xfsstats +@ xfs.log_tail.push_ail.flush value from xs_push_ail_flush field of struct xfsstats + +@ xfs.xstrat.bytes number of bytes of data processed by the XFS daemons +This is the number of bytes of file data flushed out by the XFS +flushing daemons. + +@ xfs.xstrat.quick number of buffers processed by the XFS daemons written to contiguous space on disk +This is the number of buffers flushed out by the XFS flushing daemons +which are written to contiguous space on disk. The buffers handled by +the XFS daemons are delayed allocation buffers, so this count gives an +indication of the success of the XFS daemons in allocating contiguous +disk space for the data being flushed to disk. + +@ xfs.xstrat.split number of buffers processed by the XFS daemons written to non-contiguous space on disk +This is the number of buffers flushed out by the XFS flushing daemons +which are written to non-contiguous space on disk. The buffers handled +by the XFS daemons are delayed allocation buffers, so this count gives +an indication of the failure of the XFS daemons in allocating +contiguous disk space for the data being flushed to disk. Large values +in this counter indicate that the file system has become fragmented. + +@ xfs.write number of XFS file system write operations +This is the number of write(2) system calls made to files in +XFS file systems. + +@ xfs.write_bytes number of bytes written in XFS file system write operations +This is the number of bytes written via write(2) system calls to files +in XFS file systems. It can be used in conjunction with the write_calls +count to calculate the average size of the write operations to files in +XFS file systems. + +@ xfs.read number of XFS file system read operations +This is the number of read(2) system calls made to files in XFS file +systems. + +@ xfs.read_bytes number of bytes read in XFS file system read operations +This is the number of bytes read via read(2) system calls to files in +XFS file systems. It can be used in conjunction with the read_calls +count to calculate the average size of the read operations to files in +XFS file systems. + +@ xfs.attr.get number of "get" operations on XFS extended file attributes +The number of "get" operations performed on extended file attributes +within XFS filesystems. The "get" operation retrieves the value of an +extended attribute. + +@ xfs.attr.set number of "set" operations on XFS extended file attributes +The number of "set" operations performed on extended file attributes +within XFS filesystems. The "set" operation creates and sets the value +of an extended attribute. + +@ xfs.attr.remove number of "remove" operations on XFS extended file attributes +The number of "remove" operations performed on extended file attributes +within XFS filesystems. The "remove" operation deletes an extended +attribute. + +@ xfs.attr.list number of "list" operations on XFS extended file attributes +The number of "list" operations performed on extended file attributes +within XFS filesystems. The "list" operation retrieves the set of +extended attributes associated with a file. + +@ xfs.quota.reclaims value from xs_qm_dqreclaims field of struct xfsstats +@ xfs.quota.reclaim_misses value from xs_qm_dqreclaim_misses field of struct xfsstats +@ xfs.quota.dquot_dups value from xs_qm_dquot_dups field of struct xfsstats +@ xfs.quota.cachemisses value from xs_qm_dqcachemisses field of struct xfsstats +@ xfs.quota.cachehits value from xs_qm_dqcachehits field of struct xfsstats +@ xfs.quota.wants value from xs_qm_dqwants field of struct xfsstats +@ xfs.quota.shake_reclaims value from xs_qm_dqshake_reclaims field of struct xfsstats +@ xfs.quota.inact_reclaims value from xs_qm_dqinact_reclaims field of struct xfsstats + +@ xfs.iflush_count the number of calls to xfs_iflush +This is the number of calls to xfs_iflush which gets called when an +inode is being flushed (such as by bdflush or tail pushing). +xfs_iflush searches for other inodes in the same cluster which are +dirty and flushable. + +@ xfs.icluster_flushcnt value from xs_icluster_flushcnt field of struct xfsstats + +@ xfs.icluster_flushinode number of flushes of only one inode in cluster +This is the number of times that the inode clustering was not able to +flush anything but the one inode it was called with. + +@ xfs.buffer.get number of request buffer calls +@ xfs.buffer.create number of buffers created +@ xfs.buffer.get_locked number of requests for a locked buffer which succeeded +@ xfs.buffer.get_locked_waited number of requests for a locked buffer which waited +@ xfs.buffer.miss_locked number of requests for a locked buffer which failed due to no buffer +@ xfs.buffer.busy_locked number of non-blocking requests for a locked buffer which failed +@ xfs.buffer.page_retries number of retry attempts when allocating a page for insertion in a buffer +@ xfs.buffer.page_found number of hits in the page cache when looking for a page +@ xfs.buffer.get_read number of buffer get calls requiring immediate device reads +@ xfs.vnodes.active number of vnodes not on free lists +@ xfs.vnodes.alloc number of times vn_alloc called +@ xfs.vnodes.get number of times vn_get called +@ xfs.vnodes.hold number of times vn_hold called +@ xfs.vnodes.rele number of times vn_rele called +@ xfs.vnodes.reclaim number of times vn_reclaim called +@ xfs.vnodes.remove number of times vn_remove called +@ xfs.vnodes.free number of times vn_free called +@ xfs.control.reset reset the values of all XFS metrics to zero + +@ quota.state.project.accounting 1 indicates quota accounting enabled, else 0 +@ quota.state.project.enforcement 1 indicates quotas enforced, else 0 +@ quota.project.space.hard hard limit for this project and filesys in Kbytes +@ quota.project.space.soft soft limit for this project and filesys in Kbytes +@ quota.project.space.used space used for this project and filesys in Kbytes +@ quota.project.space.time_left when soft limit is exceeded, seconds until it is enacted +@ quota.project.files.hard file count hard limit for this project and filesys +@ quota.project.files.soft file count soft limit for this project and filesys +@ quota.project.files.used file count for this project and filesys +@ quota.project.files.time_left when soft limit is exceeded, seconds until it is enacted + +@ xfs.btree.alloc_blocks.lookup +Number of free-space-by-block-number btree record lookups +@ xfs.btree.alloc_blocks.compare +Number of free-space-by-block-number btree record compares +@ xfs.btree.alloc_blocks.insrec +Number of free-space-by-block-number btree insert record operations executed +@ xfs.btree.alloc_blocks.delrec +Number of free-space-by-block-number btree delete record operations executed +@ xfs.btree.alloc_blocks.newroot +Number of times a new level is added to a free-space-by-block-number btree +@ xfs.btree.alloc_blocks.killroot +Number of times a level is removed from a free-space-by-block-number btree +@ xfs.btree.alloc_blocks.increment +Number of times a cursor has been moved forward one free-space-by-block-number +btree record +@ xfs.btree.alloc_blocks.decrement +Number of times a cursor has been moved backward one free-space-by-block-number +btree record +@ xfs.btree.alloc_blocks.lshift +Left shift block operations to make space for a new free-space-by-block-number +btree record +@ xfs.btree.alloc_blocks.rshift +Right shift block operations to make space for a new free-space-by-block-number +btree record +@ xfs.btree.alloc_blocks.split +Split block operations to make space for a new free-space-by-block-number +btree record +@ xfs.btree.alloc_blocks.join +Merge block operations when deleting free-space-by-block-number btree records +@ xfs.btree.alloc_blocks.alloc +Btree block allocations during free-space-by-block-number btree operations +@ xfs.btree.alloc_blocks.free +Btree blocks freed during free-space-by-block-number btree operations +@ xfs.btree.alloc_blocks.moves +Records moved inside blocks during free-space-by-block-number btree operations + +@ xfs.btree.alloc_contig.lookup +Number of free-space-by-size btree record lookups +@ xfs.btree.alloc_contig.compare +Number of free-space-by-size btree btree record compares +@ xfs.btree.alloc_contig.insrec +Number of free-space-by-size btree insert record operations executed +@ xfs.btree.alloc_contig.delrec +Number of free-space-by-size btree delete record operations executed +@ xfs.btree.alloc_contig.newroot +Number of times a new level is added to a free-space-by-size btree tree +@ xfs.btree.alloc_contig.killroot +Number of times a level is removed from a free-space-by-size btree tree +@ xfs.btree.alloc_contig.increment +Number of times a free-space-by-size btree cursor has been moved forward +one record +@ xfs.btree.alloc_contig.decrement +Number of times a free-space-by-size btree cursor has been moved backward +one record +@ xfs.btree.alloc_contig.lshift +Left shift block operations to make space for a new free-space-by-size +btree record +@ xfs.btree.alloc_contig.rshift +Right shift block operations to make space for a new free-space-by-size +btree record +@ xfs.btree.alloc_contig.split +Split block operations to make space for a new free-space-by-size btree +record +@ xfs.btree.alloc_contig.join +Merge block operations when deleting free-space-by-size btree records +@ xfs.btree.alloc_contig.alloc +Btree block allocations during free-space-by-size btree operations +@ xfs.btree.alloc_contig.free +Btree blocks freed during free-space-by-size btree operations +@ xfs.btree.alloc_contig.moves +Records moved inside blocks during free-space-by-size btree operations + +@ xfs.btree.block_map.lookup +Number of inode-block-map/extent btree record lookups +@ xfs.btree.block_map.compare +Number of inode-block-map/extent btree record compares +@ xfs.btree.block_map.insrec +Number of inode-block-map/extent btree insert record operations executed +@ xfs.btree.block_map.delrec +Number of inode-block-map/extent btree delete record operations executed +@ xfs.btree.block_map.newroot +Number of times a new level is added to an inode-block-map/extent btree +@ xfs.btree.block_map.killroot +Number of times a level is removed from an inode-block-map/extent btree +@ xfs.btree.block_map.increment +Number of times an inode-block-map/extent btree cursor has been moved +forward one record +@ xfs.btree.block_map.decrement +Number of times an inode-block-map/extent btree cursor has been moved +backward one record +@ xfs.btree.block_map.lshift +Left shift block operations to make space for a new inode-block-map/extent +btree record +@ xfs.btree.block_map.rshift +Right shift block operations to make space for a new inode-block-map/extent +btree record +@ xfs.btree.block_map.split +Split block operations to make space for a new inode-block-map/extent +btree record +@ xfs.btree.block_map.join +Merge block operations when deleting inode-block-map/extent btree records +@ xfs.btree.block_map.alloc +Btree block allocations during inode-block-map/extent btree operations +@ xfs.btree.block_map.free +Btree blocks freed during inode-block-map/extent btree operations +@ xfs.btree.block_map.moves +Records moved inside blocks during inode-block-map/extent btree operations + +@ xfs.btree.inode.lookup +Number of inode-allocation btree record lookups +@ xfs.btree.inode.compare +Number of inode-allocation btree record compares +@ xfs.btree.inode.insrec +Number of inode-allocation btree insert record operations executed +@ xfs.btree.inode.delrec +Number of inode-allocation btree delete record operations executed +@ xfs.btree.inode.newroot +Number of times a new level is added to an inode-allocation btree +@ xfs.btree.inode.killroot +Number of times a level is removed from an inode-allocation btree +@ xfs.btree.inode.increment +Number of times a cursor has been moved forward one inode-allocation +btree record +@ xfs.btree.inode.decrement +Number of times a cursor has been moved backward one inode-allocation +btree record +@ xfs.btree.inode.lshift +Left shift block operations to make space for a new inode-allocation +btree record +@ xfs.btree.inode.rshift +Right shift block operations to make space for a new inode-allocation +btree record +@ xfs.btree.inode.split +Split block operations to make space for a new inode-allocation btree record +@ xfs.btree.inode.join +Merge block operations when deleting inode-allocation btree records +@ xfs.btree.inode.alloc +Btree block allocations during inode-allocation btree operations +@ xfs.btree.inode.free +Btree blocks freed during inode-allocation btree operations +@ xfs.btree.inode.moves +Records moved inside blocks during inode-allocation btree operations + diff --git a/src/pmdas/linux_xfs/indom.h b/src/pmdas/linux_xfs/indom.h new file mode 100644 index 0000000..a2f51d1 --- /dev/null +++ b/src/pmdas/linux_xfs/indom.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Red Hat, Inc. All Rights Reserved. + * Copyright (c) 2005,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _INDOM_H +#define _INDOM_H + +/* + * indom serial numbers ... to manage the indom migration after the + * linux -> linux + xfs PMDAs split, these need to match the enum + * assigned values for *_INDOM from the linux PMDA. Consequently, + * the xfs indom table is sparse. + */ +#define FILESYS_INDOM 5 /* mounted filesystems */ +#define QUOTA_PRJ_INDOM 16 /* project quota */ + +#define MIN_INDOM 5 /* first indom number we use here */ +#define NUM_INDOMS 17 /* one more than highest indom number used */ + +#endif /* _INDOM_H */ diff --git a/src/pmdas/linux_xfs/linux_xfs_migrate.conf b/src/pmdas/linux_xfs/linux_xfs_migrate.conf new file mode 100644 index 0000000..59cbb64 --- /dev/null +++ b/src/pmdas/linux_xfs/linux_xfs_migrate.conf @@ -0,0 +1,16 @@ +# +# Copyright 2013 Red Hat. +# +# pmlogrewrite configuration for migrating archives containing XFS metrics +# that were captured prior to the XFS PMDA split-off from the Linux PMDA. +# Basically, the PMID domain changed from 60 (linux) to 11 (xfs) but all +# cluster and item numbers remain unchanged. +# + +# +# Migrate the domain field of the metric and indom identifiers +# +indom 60.16 { indom -> duplicate 11.16 } # need 11.16 and 60.16 +metric 60.16.* { pmid -> 11.*.* } # CLUSTER_XFS +metric 60.17.* { pmid -> 11.*.* } # CLUSTER_XFSBUF +metric 60.30.* { pmid -> 11.*.* indom -> 11.16 } # CLUSTER_QUOTA diff --git a/src/pmdas/linux_xfs/pmda.c b/src/pmdas/linux_xfs/pmda.c new file mode 100644 index 0000000..a1b937c --- /dev/null +++ b/src/pmdas/linux_xfs/pmda.c @@ -0,0 +1,979 @@ +/* + * XFS PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2000,2004,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "indom.h" +#include "domain.h" +#include "clusters.h" +#include "filesys.h" +#include "proc_fs_xfs.h" + +static int _isDSO = 1; /* for local contexts */ +static proc_fs_xfs_t proc_fs_xfs; +static char * xfs_statspath = ""; + +/* + * The XFS instance domain table is direct lookup and sparse. + * It is initialized in xfs_init(), see below. + */ +static pmdaIndom xfs_indomtab[NUM_INDOMS]; +#define INDOM(x) (xfs_indomtab[x].it_indom) + +static pmdaMetric xfs_metrictab[] = { + +/* xfs.allocs.alloc_extent */ + { &proc_fs_xfs.xs_allocx, + { PMDA_PMID(CLUSTER_XFS,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.allocs.alloc_block */ + { &proc_fs_xfs.xs_allocb, + { PMDA_PMID(CLUSTER_XFS,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.allocs.free_extent*/ + { &proc_fs_xfs.xs_freex, + { PMDA_PMID(CLUSTER_XFS,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.allocs.free_block */ + { &proc_fs_xfs.xs_freeb, + { PMDA_PMID(CLUSTER_XFS,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.alloc_btree.lookup */ + { &proc_fs_xfs.xs_abt_lookup, + { PMDA_PMID(CLUSTER_XFS,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.alloc_btree.compare */ + { &proc_fs_xfs.xs_abt_compare, + { PMDA_PMID(CLUSTER_XFS,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.alloc_btree.insrec */ + { &proc_fs_xfs.xs_abt_insrec, + { PMDA_PMID(CLUSTER_XFS,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.alloc_btree.delrec */ + { &proc_fs_xfs.xs_abt_delrec, + { PMDA_PMID(CLUSTER_XFS,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.block_map.read_ops */ + { &proc_fs_xfs.xs_blk_mapr, + { PMDA_PMID(CLUSTER_XFS,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.block_map.write_ops */ + { &proc_fs_xfs.xs_blk_mapw, + { PMDA_PMID(CLUSTER_XFS,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.block_map.unmap */ + { &proc_fs_xfs.xs_blk_unmap, + { PMDA_PMID(CLUSTER_XFS,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.block_map.add_exlist */ + { &proc_fs_xfs.xs_add_exlist, + { PMDA_PMID(CLUSTER_XFS,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.block_map.del_exlist */ + { &proc_fs_xfs.xs_del_exlist, + { PMDA_PMID(CLUSTER_XFS,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.block_map.look_exlist */ + { &proc_fs_xfs.xs_look_exlist, + { PMDA_PMID(CLUSTER_XFS,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.block_map.cmp_exlist */ + { &proc_fs_xfs.xs_cmp_exlist, + { PMDA_PMID(CLUSTER_XFS,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.bmap_btree.lookup */ + { &proc_fs_xfs.xs_bmbt_lookup, + { PMDA_PMID(CLUSTER_XFS,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.bmap_btree.compare */ + { &proc_fs_xfs.xs_bmbt_compare, + { PMDA_PMID(CLUSTER_XFS,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.bmap_btree.insrec */ + { &proc_fs_xfs.xs_bmbt_insrec, + { PMDA_PMID(CLUSTER_XFS,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.bmap_btree.delrec */ + { &proc_fs_xfs.xs_bmbt_delrec, + { PMDA_PMID(CLUSTER_XFS,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.dir_ops.lookup */ + { &proc_fs_xfs.xs_dir_lookup, + { PMDA_PMID(CLUSTER_XFS,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.dir_ops.create */ + { &proc_fs_xfs.xs_dir_create, + { PMDA_PMID(CLUSTER_XFS,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.dir_ops.remove */ + { &proc_fs_xfs.xs_dir_remove, + { PMDA_PMID(CLUSTER_XFS,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.dir_ops.getdents */ + { &proc_fs_xfs.xs_dir_getdents, + { PMDA_PMID(CLUSTER_XFS,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.transactions.sync */ + { &proc_fs_xfs.xs_trans_sync, + { PMDA_PMID(CLUSTER_XFS,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.transactions.async */ + { &proc_fs_xfs.xs_trans_async, + { PMDA_PMID(CLUSTER_XFS,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.transactions.empty */ + { &proc_fs_xfs.xs_trans_empty, + { PMDA_PMID(CLUSTER_XFS,25), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.inode_ops.ig_attempts */ + { &proc_fs_xfs.xs_ig_attempts, + { PMDA_PMID(CLUSTER_XFS,26), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.inode_ops.ig_found */ + { &proc_fs_xfs.xs_ig_found, + { PMDA_PMID(CLUSTER_XFS,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.inode_ops.ig_frecycle */ + { &proc_fs_xfs.xs_ig_frecycle, + { PMDA_PMID(CLUSTER_XFS,28), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.inode_ops.ig_missed */ + { &proc_fs_xfs.xs_ig_missed, + { PMDA_PMID(CLUSTER_XFS,29), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.inode_ops.ig_dup */ + { &proc_fs_xfs.xs_ig_dup, + { PMDA_PMID(CLUSTER_XFS,30), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.inode_ops.ig_reclaims */ + { &proc_fs_xfs.xs_ig_reclaims, + { PMDA_PMID(CLUSTER_XFS,31), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.inode_ops.ig_attrchg */ + { &proc_fs_xfs.xs_ig_attrchg, + { PMDA_PMID(CLUSTER_XFS,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.log.writes */ + { &proc_fs_xfs.xs_log_writes, + { PMDA_PMID(CLUSTER_XFS,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log.blocks */ + { &proc_fs_xfs.xs_log_blocks, + { PMDA_PMID(CLUSTER_XFS,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* xfs.log.noiclogs */ + { &proc_fs_xfs.xs_log_noiclogs, + { PMDA_PMID(CLUSTER_XFS,35), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log.force */ + { &proc_fs_xfs.xs_log_force, + { PMDA_PMID(CLUSTER_XFS,36), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log.force_sleep */ + { &proc_fs_xfs.xs_log_force_sleep, + { PMDA_PMID(CLUSTER_XFS,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.log_tail.try_logspace */ + { &proc_fs_xfs.xs_try_logspace, + { PMDA_PMID(CLUSTER_XFS,38), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.sleep_logspace */ + { &proc_fs_xfs.xs_sleep_logspace, + { PMDA_PMID(CLUSTER_XFS,39), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.pushes */ + { &proc_fs_xfs.xs_push_ail, + { PMDA_PMID(CLUSTER_XFS,40), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.success */ + { &proc_fs_xfs.xs_push_ail_success, + { PMDA_PMID(CLUSTER_XFS,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.pushbuf */ + { &proc_fs_xfs.xs_push_ail_pushbuf, + { PMDA_PMID(CLUSTER_XFS,42), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.pinned */ + { &proc_fs_xfs.xs_push_ail_pinned, + { PMDA_PMID(CLUSTER_XFS,43), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.locked */ + { &proc_fs_xfs.xs_push_ail_locked, + { PMDA_PMID(CLUSTER_XFS,44), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.flushing */ + { &proc_fs_xfs.xs_push_ail_flushing, + { PMDA_PMID(CLUSTER_XFS,45), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.restarts */ + { &proc_fs_xfs.xs_push_ail_restarts, + { PMDA_PMID(CLUSTER_XFS,46), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.log_tail.push_ail.flush */ + { &proc_fs_xfs.xs_push_ail_flush, + { PMDA_PMID(CLUSTER_XFS,47), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.xstrat.bytes */ + { &proc_fs_xfs.xpc.xs_xstrat_bytes, + { PMDA_PMID(CLUSTER_XFS,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* xfs.xstrat.quick */ + { &proc_fs_xfs.xs_xstrat_quick, + { PMDA_PMID(CLUSTER_XFS,49), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.xstrat.split */ + { &proc_fs_xfs.xs_xstrat_split, + { PMDA_PMID(CLUSTER_XFS,50), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.write */ + { &proc_fs_xfs.xs_write_calls, + { PMDA_PMID(CLUSTER_XFS,51), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.write_bytes */ + { &proc_fs_xfs.xpc.xs_write_bytes, + { PMDA_PMID(CLUSTER_XFS,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* xfs.read */ + { &proc_fs_xfs.xs_read_calls, + { PMDA_PMID(CLUSTER_XFS,53), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.read_bytes */ + { &proc_fs_xfs.xpc.xs_read_bytes, + { PMDA_PMID(CLUSTER_XFS,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, + +/* xfs.attr.get */ + { &proc_fs_xfs.xs_attr_get, + { PMDA_PMID(CLUSTER_XFS,55), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.attr.set */ + { &proc_fs_xfs.xs_attr_set, + { PMDA_PMID(CLUSTER_XFS,56), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.attr.remove */ + { &proc_fs_xfs.xs_attr_remove, + { PMDA_PMID(CLUSTER_XFS,57), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.attr.list */ + { &proc_fs_xfs.xs_attr_list, + { PMDA_PMID(CLUSTER_XFS,58), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.quota.reclaims */ + { &proc_fs_xfs.xs_qm_dqreclaims, + { PMDA_PMID(CLUSTER_XFS,59), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.reclaim_misses */ + { &proc_fs_xfs.xs_qm_dqreclaim_misses, + { PMDA_PMID(CLUSTER_XFS,60), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.dquot_dups */ + { &proc_fs_xfs.xs_qm_dquot_dups, + { PMDA_PMID(CLUSTER_XFS,61), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.cachemisses */ + { &proc_fs_xfs.xs_qm_dqcachemisses, + { PMDA_PMID(CLUSTER_XFS,62), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.cachehits */ + { &proc_fs_xfs.xs_qm_dqcachehits, + { PMDA_PMID(CLUSTER_XFS,63), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.wants */ + { &proc_fs_xfs.xs_qm_dqwants, + { PMDA_PMID(CLUSTER_XFS,64), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.shake_reclaims */ + { &proc_fs_xfs.xs_qm_dqshake_reclaims, + { PMDA_PMID(CLUSTER_XFS,65), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.quota.inact_reclaims */ + { &proc_fs_xfs.xs_qm_dqinact_reclaims, + { PMDA_PMID(CLUSTER_XFS,66), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.iflush_count */ + { &proc_fs_xfs.xs_iflush_count, + { PMDA_PMID(CLUSTER_XFS,67), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.icluster_flushcnt */ + { &proc_fs_xfs.xs_icluster_flushcnt, + { PMDA_PMID(CLUSTER_XFS,68), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.icluster_flushinode */ + { &proc_fs_xfs.xs_icluster_flushinode, + { PMDA_PMID(CLUSTER_XFS,69), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.buffer.get */ + { &proc_fs_xfs.xs_buf_get, + { PMDA_PMID(CLUSTER_XFSBUF,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.create */ + { &proc_fs_xfs.xs_buf_create, + { PMDA_PMID(CLUSTER_XFSBUF,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.get_locked */ + { &proc_fs_xfs.xs_buf_get_locked, + { PMDA_PMID(CLUSTER_XFSBUF,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.get_locked_waited */ + { &proc_fs_xfs.xs_buf_get_locked_waited, + { PMDA_PMID(CLUSTER_XFSBUF,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.busy_locked */ + { &proc_fs_xfs.xs_buf_busy_locked, + { PMDA_PMID(CLUSTER_XFSBUF,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.miss_locked */ + { &proc_fs_xfs.xs_buf_miss_locked, + { PMDA_PMID(CLUSTER_XFSBUF,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.page_retries */ + { &proc_fs_xfs.xs_buf_page_retries, + { PMDA_PMID(CLUSTER_XFSBUF,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.page_found */ + { &proc_fs_xfs.xs_buf_page_found, + { PMDA_PMID(CLUSTER_XFSBUF,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.buffer.get_read */ + { &proc_fs_xfs.xs_buf_get_read, + { PMDA_PMID(CLUSTER_XFSBUF,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.vnodes.active */ + { &proc_fs_xfs.vnodes.vn_active, + { PMDA_PMID(CLUSTER_XFS,70), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* xfs.vnodes.alloc */ + { &proc_fs_xfs.vnodes.vn_alloc, + { PMDA_PMID(CLUSTER_XFS,71), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.vnodes.get */ + { &proc_fs_xfs.vnodes.vn_get, + { PMDA_PMID(CLUSTER_XFS,72), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.vnodes.hold */ + { &proc_fs_xfs.vnodes.vn_hold, + { PMDA_PMID(CLUSTER_XFS,73), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.vnodes.rele */ + { &proc_fs_xfs.vnodes.vn_rele, + { PMDA_PMID(CLUSTER_XFS,74), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.vnodes.reclaim */ + { &proc_fs_xfs.vnodes.vn_reclaim, + { PMDA_PMID(CLUSTER_XFS,75), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.vnodes.remove */ + { &proc_fs_xfs.vnodes.vn_remove, + { PMDA_PMID(CLUSTER_XFS,76), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.vnodes.free */ + { &proc_fs_xfs.vnodes.vn_free, + { PMDA_PMID(CLUSTER_XFS,77), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.log.write_ratio */ + { &proc_fs_xfs.xs_log_write_ratio, + { PMDA_PMID(CLUSTER_XFS,78), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* xfs.control.reset */ + { NULL, + { PMDA_PMID(CLUSTER_XFS,79), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + +/* xfs.btree.alloc_blocks.lookup */ + { &proc_fs_xfs.xs_abtb_2_lookup, + { PMDA_PMID(CLUSTER_XFS,80), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.compare */ + { &proc_fs_xfs.xs_abtb_2_compare, + { PMDA_PMID(CLUSTER_XFS,81), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.insrec */ + { &proc_fs_xfs.xs_abtb_2_insrec, + { PMDA_PMID(CLUSTER_XFS,82), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.delrec */ + { &proc_fs_xfs.xs_abtb_2_delrec, + { PMDA_PMID(CLUSTER_XFS,83), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.newroot */ + { &proc_fs_xfs.xs_abtb_2_newroot, + { PMDA_PMID(CLUSTER_XFS,84), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.killroot */ + { &proc_fs_xfs.xs_abtb_2_killroot, + { PMDA_PMID(CLUSTER_XFS,85), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.increment */ + { &proc_fs_xfs.xs_abtb_2_increment, + { PMDA_PMID(CLUSTER_XFS,86), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.decrement */ + { &proc_fs_xfs.xs_abtb_2_decrement, + { PMDA_PMID(CLUSTER_XFS,87), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.lshift */ + { &proc_fs_xfs.xs_abtb_2_lshift, + { PMDA_PMID(CLUSTER_XFS,88), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.rshift */ + { &proc_fs_xfs.xs_abtb_2_rshift, + { PMDA_PMID(CLUSTER_XFS,89), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.split */ + { &proc_fs_xfs.xs_abtb_2_split, + { PMDA_PMID(CLUSTER_XFS,90), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.join */ + { &proc_fs_xfs.xs_abtb_2_join, + { PMDA_PMID(CLUSTER_XFS,91), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.alloc */ + { &proc_fs_xfs.xs_abtb_2_alloc, + { PMDA_PMID(CLUSTER_XFS,92), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.free */ + { &proc_fs_xfs.xs_abtb_2_free, + { PMDA_PMID(CLUSTER_XFS,93), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_blocks.moves */ + { &proc_fs_xfs.xs_abtb_2_moves, + { PMDA_PMID(CLUSTER_XFS,94), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.btree.alloc_contig.lookup */ + { &proc_fs_xfs.xs_abtc_2_lookup, + { PMDA_PMID(CLUSTER_XFS,95), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.compare */ + { &proc_fs_xfs.xs_abtc_2_compare, + { PMDA_PMID(CLUSTER_XFS,96), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.insrec */ + { &proc_fs_xfs.xs_abtc_2_insrec, + { PMDA_PMID(CLUSTER_XFS,97), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.delrec */ + { &proc_fs_xfs.xs_abtc_2_delrec, + { PMDA_PMID(CLUSTER_XFS,98), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.newroot */ + { &proc_fs_xfs.xs_abtc_2_newroot, + { PMDA_PMID(CLUSTER_XFS,99), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.killroot */ + { &proc_fs_xfs.xs_abtc_2_killroot, + { PMDA_PMID(CLUSTER_XFS,100), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.increment */ + { &proc_fs_xfs.xs_abtc_2_increment, + { PMDA_PMID(CLUSTER_XFS,101), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.decrement */ + { &proc_fs_xfs.xs_abtc_2_decrement, + { PMDA_PMID(CLUSTER_XFS,102), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.lshift */ + { &proc_fs_xfs.xs_abtc_2_lshift, + { PMDA_PMID(CLUSTER_XFS,103), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.rshift */ + { &proc_fs_xfs.xs_abtc_2_rshift, + { PMDA_PMID(CLUSTER_XFS,104), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.split */ + { &proc_fs_xfs.xs_abtc_2_split, + { PMDA_PMID(CLUSTER_XFS,105), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.join */ + { &proc_fs_xfs.xs_abtc_2_join, + { PMDA_PMID(CLUSTER_XFS,106), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.alloc */ + { &proc_fs_xfs.xs_abtc_2_alloc, + { PMDA_PMID(CLUSTER_XFS,107), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.free */ + { &proc_fs_xfs.xs_abtc_2_free, + { PMDA_PMID(CLUSTER_XFS,108), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.alloc_contig.moves */ + { &proc_fs_xfs.xs_abtc_2_moves, + { PMDA_PMID(CLUSTER_XFS,109), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.btree.block_map.lookup */ + { &proc_fs_xfs.xs_bmbt_2_lookup, + { PMDA_PMID(CLUSTER_XFS,110), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.compare */ + { &proc_fs_xfs.xs_bmbt_2_compare, + { PMDA_PMID(CLUSTER_XFS,111), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.insrec */ + { &proc_fs_xfs.xs_bmbt_2_insrec, + { PMDA_PMID(CLUSTER_XFS,112), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.delrec */ + { &proc_fs_xfs.xs_bmbt_2_delrec, + { PMDA_PMID(CLUSTER_XFS,113), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.newroot */ + { &proc_fs_xfs.xs_bmbt_2_newroot, + { PMDA_PMID(CLUSTER_XFS,114), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.killroot */ + { &proc_fs_xfs.xs_bmbt_2_killroot, + { PMDA_PMID(CLUSTER_XFS,115), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.increment */ + { &proc_fs_xfs.xs_bmbt_2_increment, + { PMDA_PMID(CLUSTER_XFS,116), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.decrement */ + { &proc_fs_xfs.xs_bmbt_2_decrement, + { PMDA_PMID(CLUSTER_XFS,117), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.lshift */ + { &proc_fs_xfs.xs_bmbt_2_lshift, + { PMDA_PMID(CLUSTER_XFS,118), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.rshift */ + { &proc_fs_xfs.xs_bmbt_2_rshift, + { PMDA_PMID(CLUSTER_XFS,119), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.split */ + { &proc_fs_xfs.xs_bmbt_2_split, + { PMDA_PMID(CLUSTER_XFS,120), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.join */ + { &proc_fs_xfs.xs_bmbt_2_join, + { PMDA_PMID(CLUSTER_XFS,121), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.alloc */ + { &proc_fs_xfs.xs_bmbt_2_alloc, + { PMDA_PMID(CLUSTER_XFS,122), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.free */ + { &proc_fs_xfs.xs_bmbt_2_free, + { PMDA_PMID(CLUSTER_XFS,123), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.block_map.moves */ + { &proc_fs_xfs.xs_bmbt_2_moves, + { PMDA_PMID(CLUSTER_XFS,124), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* xfs.btree.inode.lookup */ + { &proc_fs_xfs.xs_ibt_2_compare, + { PMDA_PMID(CLUSTER_XFS,125), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.compare */ + { &proc_fs_xfs.xs_ibt_2_lookup, + { PMDA_PMID(CLUSTER_XFS,126), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.insrec */ + { &proc_fs_xfs.xs_ibt_2_insrec, + { PMDA_PMID(CLUSTER_XFS,127), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.delrec */ + { &proc_fs_xfs.xs_ibt_2_delrec, + { PMDA_PMID(CLUSTER_XFS,128), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.newroot */ + { &proc_fs_xfs.xs_ibt_2_newroot, + { PMDA_PMID(CLUSTER_XFS,129), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.killroot */ + { &proc_fs_xfs.xs_ibt_2_killroot, + { PMDA_PMID(CLUSTER_XFS,130), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.increment */ + { &proc_fs_xfs.xs_ibt_2_increment, + { PMDA_PMID(CLUSTER_XFS,131), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.decrement */ + { &proc_fs_xfs.xs_ibt_2_decrement, + { PMDA_PMID(CLUSTER_XFS,132), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.lshift */ + { &proc_fs_xfs.xs_ibt_2_lshift, + { PMDA_PMID(CLUSTER_XFS,133), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.rshift */ + { &proc_fs_xfs.xs_ibt_2_rshift, + { PMDA_PMID(CLUSTER_XFS,134), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.split */ + { &proc_fs_xfs.xs_ibt_2_split, + { PMDA_PMID(CLUSTER_XFS,135), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.join */ + { &proc_fs_xfs.xs_ibt_2_join, + { PMDA_PMID(CLUSTER_XFS,136), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.alloc */ + { &proc_fs_xfs.xs_ibt_2_alloc, + { PMDA_PMID(CLUSTER_XFS,137), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.free */ + { &proc_fs_xfs.xs_ibt_2_free, + { PMDA_PMID(CLUSTER_XFS,138), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* xfs.btree.inode.moves */ + { &proc_fs_xfs.xs_ibt_2_moves, + { PMDA_PMID(CLUSTER_XFS,139), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + +/* quota.state.project.accounting */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,0), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* quota.state.project.enforcement */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,1), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* quota.project.space.hard */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,6), PM_TYPE_U64, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* quota.project.space.soft */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,7), PM_TYPE_U64, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* quota.project.space.used */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,8), PM_TYPE_U64, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* quota.project.space.time_left */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,9), PM_TYPE_32, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, }, +/* quota.project.files.hard */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,10), PM_TYPE_U64, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* quota.project.files.soft */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,11), PM_TYPE_U64, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* quota.project.files.used */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,12), PM_TYPE_U64, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* quota.project.files.time_left */ + { NULL, + { PMDA_PMID(CLUSTER_QUOTA,13), PM_TYPE_32, QUOTA_PRJ_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, }, +}; + +FILE * +xfs_statsfile(const char *path, const char *mode) +{ + char buffer[MAXPATHLEN]; + + snprintf(buffer, sizeof(buffer), "%s%s", xfs_statspath, path); + buffer[MAXPATHLEN-1] = '\0'; + return fopen(buffer, mode); +} + +static void +xfs_refresh(pmdaExt *pmda, int *need_refresh) +{ + if (need_refresh[CLUSTER_QUOTA]) + refresh_filesys(INDOM(FILESYS_INDOM), INDOM(QUOTA_PRJ_INDOM)); + if (need_refresh[CLUSTER_XFS] || need_refresh[CLUSTER_XFSBUF]) + refresh_proc_fs_xfs(&proc_fs_xfs); +} + +static int +xfs_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + __pmInDom_int *indomp = (__pmInDom_int *)&indom; + int need_refresh[NUM_CLUSTERS] = { 0 }; + + if (indomp->serial == FILESYS_INDOM || indomp->serial == QUOTA_PRJ_INDOM) + need_refresh[CLUSTER_QUOTA]++; + xfs_refresh(pmda, need_refresh); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +xfs_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + struct filesys *fs; + int sts; + + if (mdesc->m_user != NULL) { + if ((idp->cluster == CLUSTER_XFS || idp->cluster == CLUSTER_XFSBUF) && + proc_fs_xfs.errcode != 0) { + /* no values available for XFS metrics */ + return 0; + } + + switch (mdesc->m_desc.type) { + case PM_TYPE_32: + atom->l = *(__int32_t *)mdesc->m_user; + break; + case PM_TYPE_U32: + atom->ul = *(__uint32_t *)mdesc->m_user; + break; + case PM_TYPE_64: + atom->ll = *(__int64_t *)mdesc->m_user; + break; + case PM_TYPE_U64: + atom->ull = *(__uint64_t *)mdesc->m_user; + break; + case PM_TYPE_FLOAT: + atom->f = *(float *)mdesc->m_user; + break; + case PM_TYPE_DOUBLE: + atom->d = *(double *)mdesc->m_user; + break; + case PM_TYPE_STRING: + atom->cp = (char *)mdesc->m_user; + break; + default: + return 0; + } + } + else + switch (idp->cluster) { + + case CLUSTER_XFS: + switch (idp->item) { + case 79: /* xfs.control.reset */ + atom->ul = 0; + break; + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_QUOTA: + if (idp->item <= 5) { + sts = pmdaCacheLookup(INDOM(FILESYS_INDOM), inst, NULL, + (void **)&fs); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + switch (idp->item) { + case 0: /* quota.state.project.accounting */ + atom->ul = !!(fs->flags & FSF_QUOT_PROJ_ACC); + break; + case 1: /* quota.state.project.enforcement */ + atom->ul = !!(fs->flags & FSF_QUOT_PROJ_ENF); + break; + default: + return PM_ERR_PMID; + } + } + else if (idp->item <= 13) { + struct project *pp; + sts = pmdaCacheLookup(INDOM(QUOTA_PRJ_INDOM), inst, NULL, + (void **)&pp); + if (sts < 0) + return sts; + if (sts != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + switch (idp->item) { + case 6: /* quota.project.space.hard */ + atom->ull = pp->space_hard >> 1; /* BBs to KB */ + break; + case 7: /* quota.project.space.soft */ + atom->ull = pp->space_soft >> 1; /* BBs to KB */ + break; + case 8: /* quota.project.space.used */ + atom->ull = pp->space_used >> 1; /* BBs to KB */ + break; + case 9: /* quota.project.space.time_left */ + atom->l = pp->space_time_left; + break; + case 10: /* quota.project.files.hard */ + atom->ull = pp->files_hard; + break; + case 11: /* quota.project.files.soft */ + atom->ull = pp->files_soft; + break; + case 12: /* quota.project.files.used */ + atom->ull = pp->files_used; + break; + case 13: /* quota.project.files.time_left */ + atom->l = pp->files_time_left; + break; + default: + return PM_ERR_PMID; + } + } + else + return PM_ERR_PMID; + break; + } + + return 1; +} + +static int +xfs_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i, need_refresh[NUM_CLUSTERS] = { 0 }; + + for (i = 0; i < numpmid; i++) { + __pmID_int *idp = (__pmID_int *)&(pmidlist[i]); + if (idp->cluster >= MIN_CLUSTER && idp->cluster < NUM_CLUSTERS) + need_refresh[idp->cluster]++; + } + + xfs_refresh(pmda, need_refresh); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +procfs_zero(const char *filename, pmValueSet *vsp) +{ + FILE *fp; + int value; + int sts = 0; + + value = vsp->vlist[0].value.lval; + if (value < 0) + return PM_ERR_SIGN; + + fp = xfs_statsfile(filename, "w"); + if (!fp) { + sts = PM_ERR_PERMISSION; + } else { + fprintf(fp, "%d\n", value); + fclose(fp); + } + return sts; +} + +static int +xfs_store(pmResult *result, pmdaExt *pmda) +{ + int i; + int sts = 0; + pmValueSet *vsp; + __pmID_int *pmidp; + + for (i = 0; i < result->numpmid && !sts; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster == CLUSTER_XFS && pmidp->item == 79) { + if ((sts = procfs_zero("/proc/sys/fs/xfs/stats_clear", vsp)) < 0) + break; + } else { + sts = PM_ERR_PERMISSION; + break; + } + } + return sts; +} + +void +__PMDA_INIT_CALL +xfs_init(pmdaInterface *dp) +{ + char *envpath; + + if ((envpath = getenv("XFS_STATSPATH")) != NULL) + xfs_statspath = envpath; + + if (_isDSO) { + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(helppath, sizeof(helppath), "%s%c" "xfs" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_3, "XFS DSO", helppath); + } + + if (dp->status != 0) + return; + + dp->version.any.fetch = xfs_fetch; + dp->version.any.store = xfs_store; + dp->version.any.instance = xfs_instance; + pmdaSetFetchCallBack(dp, xfs_fetchCallBack); + + xfs_indomtab[FILESYS_INDOM].it_indom = FILESYS_INDOM; + xfs_indomtab[QUOTA_PRJ_INDOM].it_indom = QUOTA_PRJ_INDOM; + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, xfs_indomtab, sizeof(xfs_indomtab)/sizeof(xfs_indomtab[0]), + xfs_metrictab, sizeof(xfs_metrictab)/sizeof(xfs_metrictab[0])); + pmdaCacheOp(INDOM(FILESYS_INDOM), PMDA_CACHE_CULL); + pmdaCacheOp(INDOM(QUOTA_PRJ_INDOM), PMDA_CACHE_CULL); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:?", + .long_options = longopts, +}; + +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + + _isDSO = 0; + __pmSetProgname(argv[0]); + snprintf(helppath, sizeof(helppath), "%s%c" "xfs" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, XFS, "xfs.log", helppath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + pmdaOpenLog(&dispatch); + xfs_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/linux_xfs/proc_fs_xfs.c b/src/pmdas/linux_xfs/proc_fs_xfs.c new file mode 100644 index 0000000..6ee83d4 --- /dev/null +++ b/src/pmdas/linux_xfs/proc_fs_xfs.c @@ -0,0 +1,278 @@ +/* + * Linux /proc/fs/xfs metrics cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "proc_fs_xfs.h" + +int +refresh_proc_fs_xfs(proc_fs_xfs_t *proc_fs_xfs) +{ + char buf[4096]; + FILE *fp; + + memset(proc_fs_xfs, 0, sizeof(proc_fs_xfs_t)); + + if ((fp = xfs_statsfile("/proc/fs/xfs/stat", "r")) == NULL) + proc_fs_xfs->errcode = -oserror(); + else { + proc_fs_xfs->errcode = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "extent_alloc ", 13) == 0) + sscanf(buf, "extent_alloc %u %u %u %u", + &proc_fs_xfs->xs_allocx, + &proc_fs_xfs->xs_allocb, + &proc_fs_xfs->xs_freex, + &proc_fs_xfs->xs_freeb); + else + if (strncmp(buf, "abt ", 4) == 0) + sscanf(buf, "abt %u %u %u %u", + &proc_fs_xfs->xs_abt_lookup, + &proc_fs_xfs->xs_abt_compare, + &proc_fs_xfs->xs_abt_insrec, + &proc_fs_xfs->xs_abt_delrec); + else + if (strncmp(buf, "blk_map ", 8) == 0) + sscanf(buf, "blk_map %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_blk_mapr, + &proc_fs_xfs->xs_blk_mapw, + &proc_fs_xfs->xs_blk_unmap, + &proc_fs_xfs->xs_add_exlist, + &proc_fs_xfs->xs_del_exlist, + &proc_fs_xfs->xs_look_exlist, + &proc_fs_xfs->xs_cmp_exlist); + else + if (strncmp(buf, "bmbt ", 5) == 0) + sscanf(buf, "bmbt %u %u %u %u", + &proc_fs_xfs->xs_bmbt_lookup, + &proc_fs_xfs->xs_bmbt_compare, + &proc_fs_xfs->xs_bmbt_insrec, + &proc_fs_xfs->xs_bmbt_delrec); + else + if (strncmp(buf, "dir ", 4) == 0) + sscanf(buf, "dir %u %u %u %u", + &proc_fs_xfs->xs_dir_lookup, + &proc_fs_xfs->xs_dir_create, + &proc_fs_xfs->xs_dir_remove, + &proc_fs_xfs->xs_dir_getdents); + else + if (strncmp(buf, "trans ", 6) == 0) + sscanf(buf, "trans %u %u %u", + &proc_fs_xfs->xs_trans_sync, + &proc_fs_xfs->xs_trans_async, + &proc_fs_xfs->xs_trans_empty); + else + if (strncmp(buf, "ig ", 3) == 0) + sscanf(buf, "ig %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_ig_attempts, + &proc_fs_xfs->xs_ig_found, + &proc_fs_xfs->xs_ig_frecycle, + &proc_fs_xfs->xs_ig_missed, + &proc_fs_xfs->xs_ig_dup, + &proc_fs_xfs->xs_ig_reclaims, + &proc_fs_xfs->xs_ig_attrchg); + else + if (strncmp(buf, "log ", 4) == 0) { + sscanf(buf, "log %u %u %u %u %u", + &proc_fs_xfs->xs_log_writes, + &proc_fs_xfs->xs_log_blocks, + &proc_fs_xfs->xs_log_noiclogs, + &proc_fs_xfs->xs_log_force, + &proc_fs_xfs->xs_log_force_sleep); + } + else + if (strncmp(buf, "push_ail ", 9) == 0) + sscanf(buf, "push_ail %u %u %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_try_logspace, + &proc_fs_xfs->xs_sleep_logspace, + &proc_fs_xfs->xs_push_ail, + &proc_fs_xfs->xs_push_ail_success, + &proc_fs_xfs->xs_push_ail_pushbuf, + &proc_fs_xfs->xs_push_ail_pinned, + &proc_fs_xfs->xs_push_ail_locked, + &proc_fs_xfs->xs_push_ail_flushing, + &proc_fs_xfs->xs_push_ail_restarts, + &proc_fs_xfs->xs_push_ail_flush); + else + if (strncmp(buf, "xstrat ", 7) == 0) + sscanf(buf, "xstrat %u %u", + &proc_fs_xfs->xs_xstrat_quick, + &proc_fs_xfs->xs_xstrat_split); + else + if (strncmp(buf, "rw ", 3) == 0) + sscanf(buf, "rw %u %u", + &proc_fs_xfs->xs_write_calls, + &proc_fs_xfs->xs_read_calls); + else + if (strncmp(buf, "attr ", 5) == 0) + sscanf(buf, "attr %u %u %u %u", + &proc_fs_xfs->xs_attr_get, + &proc_fs_xfs->xs_attr_set, + &proc_fs_xfs->xs_attr_remove, + &proc_fs_xfs->xs_attr_list); + else + if (strncmp(buf, "qm ", 3) == 0) + sscanf(buf, "qm %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_qm_dqreclaims, + &proc_fs_xfs->xs_qm_dqreclaim_misses, + &proc_fs_xfs->xs_qm_dquot_dups, + &proc_fs_xfs->xs_qm_dqcachemisses, + &proc_fs_xfs->xs_qm_dqcachehits, + &proc_fs_xfs->xs_qm_dqwants, + &proc_fs_xfs->xs_qm_dqshake_reclaims, + &proc_fs_xfs->xs_qm_dqinact_reclaims); + else + if (strncmp(buf, "icluster ", 9) == 0) + sscanf(buf, "icluster %u %u %u", + &proc_fs_xfs->xs_iflush_count, + &proc_fs_xfs->xs_icluster_flushcnt, + &proc_fs_xfs->xs_icluster_flushinode); + else + if (strncmp(buf, "buf ", 4) == 0) { + sscanf(buf, "buf %u %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_buf_get, + &proc_fs_xfs->xs_buf_create, + &proc_fs_xfs->xs_buf_get_locked, + &proc_fs_xfs->xs_buf_get_locked_waited, + &proc_fs_xfs->xs_buf_busy_locked, + &proc_fs_xfs->xs_buf_miss_locked, + &proc_fs_xfs->xs_buf_page_retries, + &proc_fs_xfs->xs_buf_page_found, + &proc_fs_xfs->xs_buf_get_read); + } else + if (strncmp(buf, "vnodes ", 7) == 0) { + sscanf(buf, "vnodes %u %u %u %u %u %u %u %u", + &proc_fs_xfs->vnodes.vn_active, + &proc_fs_xfs->vnodes.vn_alloc, + &proc_fs_xfs->vnodes.vn_get, + &proc_fs_xfs->vnodes.vn_hold, + &proc_fs_xfs->vnodes.vn_rele, + &proc_fs_xfs->vnodes.vn_reclaim, + &proc_fs_xfs->vnodes.vn_remove, + &proc_fs_xfs->vnodes.vn_free); + } else + if (strncmp(buf, "abtb2 ", 6) == 0) { + sscanf(buf, "abtb2 %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_abtb_2_lookup, + &proc_fs_xfs->xs_abtb_2_compare, + &proc_fs_xfs->xs_abtb_2_insrec, + &proc_fs_xfs->xs_abtb_2_delrec, + &proc_fs_xfs->xs_abtb_2_newroot, + &proc_fs_xfs->xs_abtb_2_killroot, + &proc_fs_xfs->xs_abtb_2_increment, + &proc_fs_xfs->xs_abtb_2_decrement, + &proc_fs_xfs->xs_abtb_2_lshift, + &proc_fs_xfs->xs_abtb_2_rshift, + &proc_fs_xfs->xs_abtb_2_split, + &proc_fs_xfs->xs_abtb_2_join, + &proc_fs_xfs->xs_abtb_2_alloc, + &proc_fs_xfs->xs_abtb_2_free, + &proc_fs_xfs->xs_abtb_2_moves); + } else + if (strncmp(buf, "abtc2 ", 6) == 0) { + sscanf(buf, "abtc2 %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_abtc_2_lookup, + &proc_fs_xfs->xs_abtc_2_compare, + &proc_fs_xfs->xs_abtc_2_insrec, + &proc_fs_xfs->xs_abtc_2_delrec, + &proc_fs_xfs->xs_abtc_2_newroot, + &proc_fs_xfs->xs_abtc_2_killroot, + &proc_fs_xfs->xs_abtc_2_increment, + &proc_fs_xfs->xs_abtc_2_decrement, + &proc_fs_xfs->xs_abtc_2_lshift, + &proc_fs_xfs->xs_abtc_2_rshift, + &proc_fs_xfs->xs_abtc_2_split, + &proc_fs_xfs->xs_abtc_2_join, + &proc_fs_xfs->xs_abtc_2_alloc, + &proc_fs_xfs->xs_abtc_2_free, + &proc_fs_xfs->xs_abtc_2_moves); + } else + if (strncmp(buf, "bmbt2 ", 6) == 0) { + sscanf(buf, "bmbt2 %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_bmbt_2_lookup, + &proc_fs_xfs->xs_bmbt_2_compare, + &proc_fs_xfs->xs_bmbt_2_insrec, + &proc_fs_xfs->xs_bmbt_2_delrec, + &proc_fs_xfs->xs_bmbt_2_newroot, + &proc_fs_xfs->xs_bmbt_2_killroot, + &proc_fs_xfs->xs_bmbt_2_increment, + &proc_fs_xfs->xs_bmbt_2_decrement, + &proc_fs_xfs->xs_bmbt_2_lshift, + &proc_fs_xfs->xs_bmbt_2_rshift, + &proc_fs_xfs->xs_bmbt_2_split, + &proc_fs_xfs->xs_bmbt_2_join, + &proc_fs_xfs->xs_bmbt_2_alloc, + &proc_fs_xfs->xs_bmbt_2_free, + &proc_fs_xfs->xs_bmbt_2_moves); + } else + if (strncmp(buf, "ibt2 ", 5) == 0) { + sscanf(buf, "ibt2 %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_ibt_2_lookup, + &proc_fs_xfs->xs_ibt_2_compare, + &proc_fs_xfs->xs_ibt_2_insrec, + &proc_fs_xfs->xs_ibt_2_delrec, + &proc_fs_xfs->xs_ibt_2_newroot, + &proc_fs_xfs->xs_ibt_2_killroot, + &proc_fs_xfs->xs_ibt_2_increment, + &proc_fs_xfs->xs_ibt_2_decrement, + &proc_fs_xfs->xs_ibt_2_lshift, + &proc_fs_xfs->xs_ibt_2_rshift, + &proc_fs_xfs->xs_ibt_2_split, + &proc_fs_xfs->xs_ibt_2_join, + &proc_fs_xfs->xs_ibt_2_alloc, + &proc_fs_xfs->xs_ibt_2_free, + &proc_fs_xfs->xs_ibt_2_moves); + } else + if (strncmp(buf, "xpc", 3) == 0) + sscanf(buf, "xpc %llu %llu %llu", + (unsigned long long *)&proc_fs_xfs->xpc.xs_xstrat_bytes, + (unsigned long long *)&proc_fs_xfs->xpc.xs_write_bytes, + (unsigned long long *)&proc_fs_xfs->xpc.xs_read_bytes); + } + fclose(fp); + + if (proc_fs_xfs->xs_log_writes) + proc_fs_xfs->xs_log_write_ratio = + proc_fs_xfs->xs_log_blocks / proc_fs_xfs->xs_log_writes; + /* + * Bug #824382. xs_log_blocks is counted in units + * of 512 bytes/block, but PCP exports it as Kbytes. + */ + proc_fs_xfs->xs_log_blocks >>= 1; + + fp = xfs_statsfile("/proc/fs/xfs/xqmstat", "r"); + if (fp != (FILE *)NULL) { + if (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp(buf, "qm", 2) == 0) + sscanf(buf, "qm %u %u %u %u %u %u %u %u", + &proc_fs_xfs->xs_qm_dqreclaims, + &proc_fs_xfs->xs_qm_dqreclaim_misses, + &proc_fs_xfs->xs_qm_dquot_dups, + &proc_fs_xfs->xs_qm_dqcachemisses, + &proc_fs_xfs->xs_qm_dqcachehits, + &proc_fs_xfs->xs_qm_dqwants, + &proc_fs_xfs->xs_qm_dqshake_reclaims, + &proc_fs_xfs->xs_qm_dqinact_reclaims); + } + fclose(fp); + } + } + + if (proc_fs_xfs->errcode == 0) + return 0; + return -1; +} diff --git a/src/pmdas/linux_xfs/proc_fs_xfs.h b/src/pmdas/linux_xfs/proc_fs_xfs.h new file mode 100644 index 0000000..bec0514 --- /dev/null +++ b/src/pmdas/linux_xfs/proc_fs_xfs.h @@ -0,0 +1,189 @@ +/* + * Linux /proc/fs/xfs metrics cluster + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2010 Aconex. All Rights Reserved. + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +typedef struct { + int errcode; /* error from previous refresh */ + unsigned int xs_allocx; /* allocs.alloc_extent */ + unsigned int xs_allocb; /* allocs.alloc_block */ + unsigned int xs_freex; /* allocs.free_extent */ + unsigned int xs_freeb; /* allocs.free_block */ + + unsigned int xs_abt_lookup; /* alloc_btree.lookup */ + unsigned int xs_abt_compare; /* alloc_btree.compare */ + unsigned int xs_abt_insrec; /* alloc_btree.insrec */ + unsigned int xs_abt_delrec; /* alloc_btree.delrec */ + unsigned int xs_blk_mapr; /* block_map.read_ops */ + unsigned int xs_blk_mapw; /* block_map.write_ops */ + unsigned int xs_blk_unmap; /* block_map.unmap */ + unsigned int xs_add_exlist; /* block_map.add_exlist */ + unsigned int xs_del_exlist; /* block_map.del_exlist */ + unsigned int xs_look_exlist; /* block_map.look_exlist */ + unsigned int xs_cmp_exlist; /* block_map.cmp_exlist */ + unsigned int xs_bmbt_lookup; /* bmap_btree.lookup */ + unsigned int xs_bmbt_compare; /* bmap_btree.compare */ + unsigned int xs_bmbt_insrec; /* bmap_btree.insrec */ + unsigned int xs_bmbt_delrec; /* bmap_btree.delrec */ + + unsigned int xs_dir_lookup; /* dir_ops.lookup */ + unsigned int xs_dir_create; /* dir_ops.create */ + unsigned int xs_dir_remove; /* dir_ops.remove */ + unsigned int xs_dir_getdents; /* dir_ops.getdents */ + + unsigned int xs_trans_sync; /* transactions.sync */ + unsigned int xs_trans_async; /* transactions.async */ + unsigned int xs_trans_empty; /* transactions.empty */ + + unsigned int xs_ig_attempts; /* inode_ops.ig_attempts */ + unsigned int xs_ig_found; /* inode_ops.ig_found */ + unsigned int xs_ig_frecycle; /* inode_ops.ig_frecycle */ + unsigned int xs_ig_missed; /* inode_ops.ig_missed */ + unsigned int xs_ig_dup; /* inode_ops.ig_dup */ + unsigned int xs_ig_reclaims; /* inode_ops.ig_reclaims */ + unsigned int xs_ig_attrchg; /* inode_ops.ig_attrchg */ + + unsigned int xs_log_writes; /* log.writes */ + unsigned int xs_log_blocks; /* log.blocks */ + float xs_log_write_ratio; /* log.write_ratio */ + unsigned int xs_log_noiclogs; /* log.noiclogs */ + + unsigned int xs_xstrat_quick; /* xstrat.quick */ + unsigned int xs_xstrat_split; /* xstrat.split */ + unsigned int xs_write_calls; /* write */ + unsigned int xs_read_calls; /* read */ + + unsigned int xs_attr_get; /* attr.get */ + unsigned int xs_attr_set; /* attr.set */ + unsigned int xs_attr_remove; /* attr.remove */ + unsigned int xs_attr_list; /* attr.list */ + + unsigned int xs_log_force; /* log.force */ + unsigned int xs_log_force_sleep; /* log.force_sleep */ + unsigned int xs_try_logspace; /* log_tail.try_logspace */ + unsigned int xs_sleep_logspace; /* log_tail.sleep_logspace */ + unsigned int xs_push_ail; /* log_tail.push_ail.pushes */ + unsigned int xs_push_ail_success; /* log_tail.push_ail.success */ + unsigned int xs_push_ail_pushbuf; /* log_tail.push_ail.pushbuf */ + unsigned int xs_push_ail_pinned; /* log_tail.push_ail.pinned */ + unsigned int xs_push_ail_locked; /* log_tail.push_ail.locked */ + unsigned int xs_push_ail_flushing; /* log_tail.push_ail.flushing */ + unsigned int xs_push_ail_restarts; /* log_tail.push_ail.restarts */ + unsigned int xs_push_ail_flush; /* log_tail.push_ail.flush */ + + unsigned int xs_qm_dqreclaims; /* quota.reclaims */ + unsigned int xs_qm_dqreclaim_misses; /* quota.reclaim_misses */ + unsigned int xs_qm_dquot_dups; /* quota.dquot_dups */ + unsigned int xs_qm_dqcachemisses; /* quota.cachemisses */ + unsigned int xs_qm_dqcachehits; /* quota.cachehits */ + unsigned int xs_qm_dqwants; /* quota.wants */ + unsigned int xs_qm_dqshake_reclaims; /* quota.shake_reclaims */ + unsigned int xs_qm_dqinact_reclaims; /* quota.inact_reclaims */ + + unsigned int xs_iflush_count; /* iflush_count */ + unsigned int xs_icluster_flushcnt; /* icluster_flushcnt */ + unsigned int xs_icluster_flushinode; /* icluster_flushinode */ + + unsigned int xs_buf_get; /* buffer.get */ + unsigned int xs_buf_create; /* buffer.create */ + unsigned int xs_buf_get_locked; /* buffer.get_locked */ + unsigned int xs_buf_get_locked_waited; /* buffer.get_locked_waited */ + unsigned int xs_buf_busy_locked; /* buffer.busy_locked */ + unsigned int xs_buf_miss_locked; /* buffer.miss_locked */ + unsigned int xs_buf_page_retries; /* buffer.page_retries */ + unsigned int xs_buf_page_found; /* buffer.page_found */ + unsigned int xs_buf_get_read; /* buffer.get_read */ + + unsigned int xs_abtb_2_lookup; /* btree.alloc_blocks.lookup */ + unsigned int xs_abtb_2_compare; /* btree.alloc_blocks.compare */ + unsigned int xs_abtb_2_insrec; /* btree.alloc_blocks.insrec */ + unsigned int xs_abtb_2_delrec; /* btree.alloc_blocks.delrec */ + unsigned int xs_abtb_2_newroot; /* btree.alloc_blocks.newroot */ + unsigned int xs_abtb_2_killroot; /* btree.alloc_blocks.killroot */ + unsigned int xs_abtb_2_increment; /* btree.alloc_blocks.increment */ + unsigned int xs_abtb_2_decrement; /* btree.alloc_blocks.decrement */ + unsigned int xs_abtb_2_lshift; /* btree.alloc_blocks.lshift */ + unsigned int xs_abtb_2_rshift; /* btree.alloc_blocks.rshift */ + unsigned int xs_abtb_2_split; /* btree.alloc_blocks.split */ + unsigned int xs_abtb_2_join; /* btree.alloc_blocks.join */ + unsigned int xs_abtb_2_alloc; /* btree.alloc_blocks.alloc */ + unsigned int xs_abtb_2_free; /* btree.alloc_blocks.free */ + unsigned int xs_abtb_2_moves; /* btree.alloc_blocks.moves */ + unsigned int xs_abtc_2_lookup; /* btree.alloc_contig.lookup */ + unsigned int xs_abtc_2_compare; /* btree.alloc_contig.compare */ + unsigned int xs_abtc_2_insrec; /* btree.alloc_contig.insrec */ + unsigned int xs_abtc_2_delrec; /* btree.alloc_contig.delrec */ + unsigned int xs_abtc_2_newroot; /* btree.alloc_contig.newroot */ + unsigned int xs_abtc_2_killroot; /* btree.alloc_contig.killroot */ + unsigned int xs_abtc_2_increment; /* btree.alloc_contig.increment */ + unsigned int xs_abtc_2_decrement; /* btree.alloc_contig.decrement */ + unsigned int xs_abtc_2_lshift; /* btree.alloc_contig.lshift */ + unsigned int xs_abtc_2_rshift; /* btree.alloc_contig.rshift */ + unsigned int xs_abtc_2_split; /* btree.alloc_contig.split */ + unsigned int xs_abtc_2_join; /* btree.alloc_contig.join */ + unsigned int xs_abtc_2_alloc; /* btree.alloc_contig.alloc */ + unsigned int xs_abtc_2_free; /* btree.alloc_contig.free */ + unsigned int xs_abtc_2_moves; /* btree.alloc_contig.moves */ + unsigned int xs_bmbt_2_lookup; /* btree.block_map.lookup */ + unsigned int xs_bmbt_2_compare; /* btree.block_map.compare */ + unsigned int xs_bmbt_2_insrec; /* btree.block_map.insrec */ + unsigned int xs_bmbt_2_delrec; /* btree.block_map.delrec */ + unsigned int xs_bmbt_2_newroot; /* btree.block_map.newroot */ + unsigned int xs_bmbt_2_killroot; /* btree.block_map.killroot */ + unsigned int xs_bmbt_2_increment; /* btree.block_map.increment */ + unsigned int xs_bmbt_2_decrement; /* btree.block_map.decrement */ + unsigned int xs_bmbt_2_lshift; /* btree.block_map.lshift */ + unsigned int xs_bmbt_2_rshift; /* btree.block_map.rshift */ + unsigned int xs_bmbt_2_split; /* btree.block_map.split */ + unsigned int xs_bmbt_2_join; /* btree.block_map.join */ + unsigned int xs_bmbt_2_alloc; /* btree.block_map.alloc */ + unsigned int xs_bmbt_2_free; /* btree.block_map.free */ + unsigned int xs_bmbt_2_moves; /* btree.block_map.moves */ + unsigned int xs_ibt_2_lookup; /* btree.inode.lookup */ + unsigned int xs_ibt_2_compare; /* btree.inode.compare */ + unsigned int xs_ibt_2_insrec; /* btree.inode.insrec */ + unsigned int xs_ibt_2_delrec; /* btree.inode.delrec */ + unsigned int xs_ibt_2_newroot; /* btree.inode.newroot */ + unsigned int xs_ibt_2_killroot; /* btree.inode.killroot */ + unsigned int xs_ibt_2_increment; /* btree.inode.increment */ + unsigned int xs_ibt_2_decrement; /* btree.inode.decrement */ + unsigned int xs_ibt_2_lshift; /* btree.inode.lshift */ + unsigned int xs_ibt_2_rshift; /* btree.inode.rshift */ + unsigned int xs_ibt_2_split; /* btree.inode.split */ + unsigned int xs_ibt_2_join; /* btree.inode.join */ + unsigned int xs_ibt_2_alloc; /* btree.inode.alloc */ + unsigned int xs_ibt_2_free; /* btree.inode.free */ + unsigned int xs_ibt_2_moves; /* btree.inode.moves */ + + struct vnodes { + unsigned int vn_active; /* vnodes.active */ + unsigned int vn_alloc; /* vnodes.alloc */ + unsigned int vn_get; /* vnodes.get */ + unsigned int vn_hold; /* vnodes.hold */ + unsigned int vn_rele; /* vnodes.rele */ + unsigned int vn_reclaim; /* vnodes.reclaim */ + unsigned int vn_remove; /* vnodes.remove */ + unsigned int vn_free; /* vnodes.free */ + } vnodes; + struct xpc { + __uint64_t xs_write_bytes; /* write_bytes */ + __uint64_t xs_read_bytes; /* read_bytes */ + __uint64_t xs_xstrat_bytes; /* xstrat_bytes */ + } xpc; +} proc_fs_xfs_t; + +extern FILE *xfs_statsfile(const char *, const char *); +extern int refresh_proc_fs_xfs(proc_fs_xfs_t *); diff --git a/src/pmdas/linux_xfs/root b/src/pmdas/linux_xfs/root new file mode 100644 index 0000000..5f26a89 --- /dev/null +++ b/src/pmdas/linux_xfs/root @@ -0,0 +1,6 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include +#include "root_xfs" diff --git a/src/pmdas/linux_xfs/root_xfs b/src/pmdas/linux_xfs/root_xfs new file mode 100644 index 0000000..16ebfd5 --- /dev/null +++ b/src/pmdas/linux_xfs/root_xfs @@ -0,0 +1,295 @@ +/* + * Portions Copyright (c) 2013 Red Hat. + * Copyright (c) 2000,2004,2007-2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef XFS +#define XFS 11 +#endif + +root { + xfs + quota +} + +xfs { + allocs + alloc_btree + block_map + bmap_btree + dir_ops + transactions + inode_ops + log + log_tail + xstrat + write XFS:16:51 + write_bytes XFS:16:52 + read XFS:16:53 + read_bytes XFS:16:54 + attr + quota + iflush_count XFS:16:67 + icluster_flushcnt XFS:16:68 + icluster_flushinode XFS:16:69 + buffer + vnodes + control + btree +} + +xfs.allocs { + alloc_extent XFS:16:0 + alloc_block XFS:16:1 + free_extent XFS:16:2 + free_block XFS:16:3 +} + + +xfs.alloc_btree { + lookup XFS:16:4 + compare XFS:16:5 + insrec XFS:16:6 + delrec XFS:16:7 +} + +xfs.block_map { + read_ops XFS:16:8 + write_ops XFS:16:9 + unmap XFS:16:10 + add_exlist XFS:16:11 + del_exlist XFS:16:12 + look_exlist XFS:16:13 + cmp_exlist XFS:16:14 +} + +xfs.bmap_btree { + lookup XFS:16:15 + compare XFS:16:16 + insrec XFS:16:17 + delrec XFS:16:18 +} + +xfs.dir_ops { + lookup XFS:16:19 + create XFS:16:20 + remove XFS:16:21 + getdents XFS:16:22 +} + +xfs.transactions { + sync XFS:16:23 + async XFS:16:24 + empty XFS:16:25 +} + +xfs.inode_ops { + ig_attempts XFS:16:26 + ig_found XFS:16:27 + ig_frecycle XFS:16:28 + ig_missed XFS:16:29 + ig_dup XFS:16:30 + ig_reclaims XFS:16:31 + ig_attrchg XFS:16:32 +} + +xfs.log { + writes XFS:16:33 + blocks XFS:16:34 + write_ratio XFS:16:78 + noiclogs XFS:16:35 + force XFS:16:36 + force_sleep XFS:16:37 +} + +xfs.log_tail { + try_logspace XFS:16:38 + sleep_logspace XFS:16:39 + push_ail +} + +xfs.log_tail.push_ail { + pushes XFS:16:40 + success XFS:16:41 + pushbuf XFS:16:42 + pinned XFS:16:43 + locked XFS:16:44 + flushing XFS:16:45 + restarts XFS:16:46 + flush XFS:16:47 +} + +xfs.xstrat { + bytes XFS:16:48 + quick XFS:16:49 + split XFS:16:50 +} + +xfs.attr { + get XFS:16:55 + set XFS:16:56 + remove XFS:16:57 + list XFS:16:58 +} + +xfs.quota { + reclaims XFS:16:59 + reclaim_misses XFS:16:60 + dquot_dups XFS:16:61 + cachemisses XFS:16:62 + cachehits XFS:16:63 + wants XFS:16:64 + shake_reclaims XFS:16:65 + inact_reclaims XFS:16:66 +} + +xfs.vnodes { + active XFS:16:70 + alloc XFS:16:71 + get XFS:16:72 + hold XFS:16:73 + rele XFS:16:74 + reclaim XFS:16:75 + remove XFS:16:76 + free XFS:16:77 +} + +xfs.control { + reset XFS:16:79 +} + +xfs.buffer { + get XFS:17:0 + create XFS:17:1 + get_locked XFS:17:2 + get_locked_waited XFS:17:3 + busy_locked XFS:17:4 + miss_locked XFS:17:5 + page_retries XFS:17:6 + page_found XFS:17:7 + get_read XFS:17:8 +} + +xfs.btree { + alloc_blocks + alloc_contig + block_map + inode +} + +xfs.btree.alloc_blocks { + lookup XFS:16:80 + compare XFS:16:81 + insrec XFS:16:82 + delrec XFS:16:83 + newroot XFS:16:84 + killroot XFS:16:85 + increment XFS:16:86 + decrement XFS:16:87 + lshift XFS:16:88 + rshift XFS:16:89 + split XFS:16:90 + join XFS:16:91 + alloc XFS:16:92 + free XFS:16:93 + moves XFS:16:94 +} + +xfs.btree.alloc_contig { + lookup XFS:16:95 + compare XFS:16:96 + insrec XFS:16:97 + delrec XFS:16:98 + newroot XFS:16:99 + killroot XFS:16:100 + increment XFS:16:101 + decrement XFS:16:102 + lshift XFS:16:103 + rshift XFS:16:104 + split XFS:16:105 + join XFS:16:106 + alloc XFS:16:107 + free XFS:16:108 + moves XFS:16:109 +} + +xfs.btree.block_map { + lookup XFS:16:110 + compare XFS:16:111 + insrec XFS:16:112 + delrec XFS:16:113 + newroot XFS:16:114 + killroot XFS:16:115 + increment XFS:16:116 + decrement XFS:16:117 + lshift XFS:16:118 + rshift XFS:16:119 + split XFS:16:120 + join XFS:16:121 + alloc XFS:16:122 + free XFS:16:123 + moves XFS:16:124 +} + +xfs.btree.inode { + lookup XFS:16:125 + compare XFS:16:126 + insrec XFS:16:127 + delrec XFS:16:128 + newroot XFS:16:129 + killroot XFS:16:130 + increment XFS:16:131 + decrement XFS:16:132 + lshift XFS:16:133 + rshift XFS:16:134 + split XFS:16:135 + join XFS:16:136 + alloc XFS:16:137 + free XFS:16:138 + moves XFS:16:139 +} + +quota { + state + project +} + +quota.state { + project +} + +quota.state.project { + accounting XFS:30:0 + enforcement XFS:30:1 +} + +quota.project { + space + files +} + +quota.project.space { + hard XFS:30:6 + soft XFS:30:7 + used XFS:30:8 + time_left XFS:30:9 +} + +quota.project.files { + hard XFS:30:10 + soft XFS:30:11 + used XFS:30:12 + time_left XFS:30:13 +} + +#undef XFS diff --git a/src/pmdas/lmsensors/GNUmakefile b/src/pmdas/lmsensors/GNUmakefile new file mode 100644 index 0000000..15df27c --- /dev/null +++ b/src/pmdas/lmsensors/GNUmakefile @@ -0,0 +1,61 @@ +# +# Original implementation by Troy Dawson (dawson@fnal.gov) +# +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = lmsensors +DOMAIN = LMSENSORS +TARGETS = $(IAM) +CFILES = lmsensors.c +HFILES = lmsensors.h +SCRIPTS = Install Remove +DFILES = README +LSRCFILES = $(SCRIPTS) pmns help root $(DFILES) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log pmda$(IAM) pmda_$(IAM).so $(TARGETS) \ + help.pag help.dir +LLDLIBS = $(PCP_PMDALIB) + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: $(TARGETS) + +install : default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) pmns help root domain.h $(PMDADIR) +else +build-me: +install: +endif + +$(IAM): $(OBJECTS) + +lmsensors.o: domain.h + +default_pcp: default + +install_pcp: install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/lmsensors/Install b/src/pmdas/lmsensors/Install new file mode 100755 index 0000000..977f06e --- /dev/null +++ b/src/pmdas/lmsensors/Install @@ -0,0 +1,35 @@ +#! /bin/sh +# +# Original implementation by Troy Dawson (dawson@fnal.gov) +# +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=lmsensors +pmda_interface=2 +forced_restart=true + +# Do it +# +pmdaSetup + +dso_opt=true +socket_opt=true +socket_inet_def=2079 + +pmdaInstall + +exit 0 diff --git a/src/pmdas/lmsensors/README b/src/pmdas/lmsensors/README new file mode 100644 index 0000000..961d26a --- /dev/null +++ b/src/pmdas/lmsensors/README @@ -0,0 +1,69 @@ +# +# Original implementation by Troy Dawson (dawson@fnal.gov) +# +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# + +lmsensors PMDA +============== + +This PMDA exports information about the lm sensors on +compatible motherboards. + +This source code was contributed by Troy Dawson (dawson@fnal.gov) +to the PCP open source project. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT lmsensors + +Installation +============ + + + # cd $PCP_PMDAS_DIR/lmsensors + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use + ($PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + + + Alternatively, to install just the Performance Metrics Name Space + for the lmsensors metrics on the local system, but not the lmsensors PMDA + (presumably because the local system is running PCP 1.x and you + wish to connect to a remote system where PCP 2.0 and the lmsensors PMDA + is running), make sure the Performance Metrics Domain defined in + ./domain.h matches the domain chosen for the lmsensors PMDA on the + remote system (check the second field in the corresponding line of + the $PCP_PMCDCONF_PATH file on the remote system), then + + # ./Install -N + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/lmsensors + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/lmsensors.log) should be checked for any warnings or + errors. diff --git a/src/pmdas/lmsensors/Remove b/src/pmdas/lmsensors/Remove new file mode 100755 index 0000000..ddcbfc0 --- /dev/null +++ b/src/pmdas/lmsensors/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Original implementation by Troy Dawson (dawson@fnal.gov) +# +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=lmsensors + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/lmsensors/help b/src/pmdas/lmsensors/help new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/src/pmdas/lmsensors/help @@ -0,0 +1 @@ +# diff --git a/src/pmdas/lmsensors/lmsensors.c b/src/pmdas/lmsensors/lmsensors.c new file mode 100644 index 0000000..c8891f0 --- /dev/null +++ b/src/pmdas/lmsensors/lmsensors.c @@ -0,0 +1,953 @@ +/* + * lmsensors, configurable PMDA + * + * Original implementation by Troy Dawson (dawson@fnal.gov) + * + * Copyright (c) 2012,2014 Red Hat. + * Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "lmsensors.h" + +static char *username; +static char buf[4096]; +static chips schips; + +/* + * lmsensors PMDA + * + */ + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +static pmdaMetric metrictab[] = { +/* n_total */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* n_lm75 */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* n_lm79 */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* n_lm87 */ + { NULL, + { PMDA_PMID(0,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* n_w83781d */ + { NULL, + { PMDA_PMID(0,4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* n_mtp008 */ + { NULL, + { PMDA_PMID(0,5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm75 temp */ + { NULL, + { PMDA_PMID(1,0), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 fan1 */ + { NULL, + { PMDA_PMID(2,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 fan2 */ + { NULL, + { PMDA_PMID(2,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 fan3 */ + { NULL, + { PMDA_PMID(2,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 fan_div */ + { NULL, + { PMDA_PMID(2,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 temp */ + { NULL, + { PMDA_PMID(2,4), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 alarms */ + { NULL, + { PMDA_PMID(2,5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 VCore1 */ + { NULL, + { PMDA_PMID(2,6), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 VCore2 */ + { NULL, + { PMDA_PMID(2,7), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 p33V */ + { NULL, + { PMDA_PMID(2,8), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 p5V */ + { NULL, + { PMDA_PMID(2,9), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 p12V */ + { NULL, + { PMDA_PMID(2,10), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 n12V */ + { NULL, + { PMDA_PMID(2,11), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 n5V */ + { NULL, + { PMDA_PMID(2,12), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm79 vid */ + { NULL, + { PMDA_PMID(2,13), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 fan1 */ + { NULL, + { PMDA_PMID(3,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 fan2 */ + { NULL, + { PMDA_PMID(3,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 temp1 */ + { NULL, + { PMDA_PMID(3,2), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 CPUtemp */ + { NULL, + { PMDA_PMID(3,3), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 Vccp1 */ + { NULL, + { PMDA_PMID(3,4), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 Vccp2 */ + { NULL, + { PMDA_PMID(3,5), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 p25V */ + { NULL, + { PMDA_PMID(3,6), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 p33V */ + { NULL, + { PMDA_PMID(3,7), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 p5V */ + { NULL, + { PMDA_PMID(3,8), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 p12V */ + { NULL, + { PMDA_PMID(3,9), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* lm87 vid */ + { NULL, + { PMDA_PMID(3,10), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d fan1 */ + { NULL, + { PMDA_PMID(4,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d fan2 */ + { NULL, + { PMDA_PMID(4,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d fan3 */ + { NULL, + { PMDA_PMID(4,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d fan_div */ + { NULL, + { PMDA_PMID(4,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d temp1 */ + { NULL, + { PMDA_PMID(4,4), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d temp2 */ + { NULL, + { PMDA_PMID(4,5), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d temp3 */ + { NULL, + { PMDA_PMID(4,6), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d alarms */ + { NULL, + { PMDA_PMID(4,7), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d beep */ + { NULL, + { PMDA_PMID(4,8), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d VCore1 */ + { NULL, + { PMDA_PMID(4,9), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d VCore2 */ + { NULL, + { PMDA_PMID(4,10), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d p33V */ + { NULL, + { PMDA_PMID(4,11), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d p5V */ + { NULL, + { PMDA_PMID(4,12), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d p12V */ + { NULL, + { PMDA_PMID(4,13), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d n12V */ + { NULL, + { PMDA_PMID(4,14), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d n5V */ + { NULL, + { PMDA_PMID(4,15), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* w83781d vid */ + { NULL, + { PMDA_PMID(4,16), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 fan1 */ + { NULL, + { PMDA_PMID(5,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 fan2 */ + { NULL, + { PMDA_PMID(5,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 fan3 */ + { NULL, + { PMDA_PMID(5,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 temp1 */ + { NULL, + { PMDA_PMID(5,3), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 temp2 */ + { NULL, + { PMDA_PMID(5,4), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 VCore1 */ + { NULL, + { PMDA_PMID(5,5), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 VCore2 */ + { NULL, + { PMDA_PMID(5,6), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 p33V */ + { NULL, + { PMDA_PMID(5,7), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 p12V */ + { NULL, + { PMDA_PMID(5,8), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 n12V */ + { NULL, + { PMDA_PMID(5,9), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 vid */ + { NULL, + { PMDA_PMID(5,10), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +/* mtp008 vtt */ + { NULL, + { PMDA_PMID(5,11), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, }, +}; + + +w83781d get_w83781d() +{ + float f; + w83781d sensor= {0,0,0,0,00.01,00.01,00.01,0,0,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01}; + + if (schips.n_w83781d > 0) { + +/* fan1 */ + get_file(schips.s_w83781d[0],"/fan1"); + sensor.fan1 = get_int(buf,2); +/* fan2 */ + get_file(schips.s_w83781d[0],"/fan2"); + sensor.fan2 = get_int(buf,2); +/* fan3 */ + get_file(schips.s_w83781d[0],"/fan3"); + sensor.fan3 = get_int(buf,2); +/* fan_div */ + get_file(schips.s_w83781d[0],"/fan_div"); + sensor.fan_div = get_int(buf,1); +/* temp1 */ + get_file(schips.s_w83781d[0],"/temp1"); + sensor.temp1 = get_float(buf,3); +/* temp2 */ + get_file(schips.s_w83781d[0],"/temp2"); + sensor.temp2 = get_float(buf,3); +/* temp3 */ + get_file(schips.s_w83781d[0],"/temp3"); + sensor.temp3 = get_float(buf,3); +/* alarms */ + get_file(schips.s_w83781d[0],"/alarms"); + sensor.alarms = get_int(buf,1); +/* beep */ + get_file(schips.s_w83781d[0],"/beep"); + sensor.beep = get_int(buf,1); +/* VCore1 */ + get_file(schips.s_w83781d[0],"/in0"); + sensor.VCore1 = get_float(buf,3); +/* VCore2 */ + get_file(schips.s_w83781d[0],"/in1"); + sensor.VCore2 = get_float(buf,3); +/* p33V */ + get_file(schips.s_w83781d[0],"/in2"); + sensor.p33V = get_float(buf,3); +/* p5V */ + get_file(schips.s_w83781d[0],"/in3"); + f = get_float(buf,3); + sensor.p5V = f * ((6.80/10)+1); +/* p12V */ + get_file(schips.s_w83781d[0],"/in4"); + f = get_float(buf,3); + sensor.p12V = f * ((28.00/10)+1); +/* n12V */ + get_file(schips.s_w83781d[0],"/in5"); + f = get_float(buf,3); + sensor.n12V = -1 * f * (210/60.40); +/* n5V */ + get_file(schips.s_w83781d[0],"/in6"); + f = get_float(buf,3); + sensor.n5V = -1 * f * (90.9/60.40); +/* vid */ + get_file(schips.s_w83781d[0],"/vid"); + sensor.vid = get_float(buf,1); + } + return sensor; +} + +mtp008 get_mtp008() +{ + float f; + mtp008 sensor= {0,0,0,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01}; + + if (schips.n_mtp008 > 0) { + +/* fan1 */ + get_file(schips.s_mtp008[0],"/fan1"); + sensor.fan1 = get_int(buf,2); +/* fan2 */ + get_file(schips.s_mtp008[0],"/fan2"); + sensor.fan2 = get_int(buf,2); +/* fan3 */ + get_file(schips.s_mtp008[0],"/fan3"); + sensor.fan3 = get_int(buf,2); +/* temp1 */ + get_file(schips.s_mtp008[0],"/temp1"); + sensor.temp1 = get_float(buf,3); +/* temp2 */ + get_file(schips.s_mtp008[0],"/temp2"); + sensor.temp2 = get_float(buf,3); +/* VCore1 */ + get_file(schips.s_mtp008[0],"/in0"); + sensor.VCore1 = get_float(buf,3); +/* VCore2 */ + get_file(schips.s_mtp008[0],"/in3"); + sensor.VCore2 = get_float(buf,3); +/* p33V */ + get_file(schips.s_mtp008[0],"/in1"); + sensor.p33V = get_float(buf,3); +/* p12V */ + get_file(schips.s_mtp008[0],"/in2"); + f = get_float(buf,3); + sensor.p12V = f * ((38.00/10)+1); +/* n12V */ + get_file(schips.s_mtp008[0],"/in5"); + f = get_float(buf,3); + sensor.n12V = ( f * 36 - 118.61 ) / 7; +/* vid */ + get_file(schips.s_mtp008[0],"/vid"); + sensor.vid = get_float(buf,1); +/* vtt */ + get_file(schips.s_mtp008[0],"/in6"); + sensor.vid = get_float(buf,3); + } + return sensor; +} + +lm79 get_lm79() +{ + float f; + lm79 sensor= {0,0,0,00.01,0,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01}; + + if (schips.n_lm79 > 0) { +/* fan1 */ + get_file(schips.s_lm79[0],"/fan1"); + sensor.fan1 = get_int(buf,2); +/* fan2 */ + get_file(schips.s_lm79[0],"/fan2"); + sensor.fan2 = get_int(buf,2); +/* fan3 */ + get_file(schips.s_lm79[0],"/fan3"); + sensor.fan3 = get_int(buf,2); +/* fan_div */ + get_file(schips.s_lm79[0],"/fan_div"); + sensor.fan_div = get_int(buf,1); +/* temp */ + get_file(schips.s_lm79[0],"/temp"); + sensor.temp = get_float(buf,3); +/* alarms */ + get_file(schips.s_lm79[0],"/alarms"); + sensor.alarms = get_int(buf,1); +/* VCore1 */ + get_file(schips.s_lm79[0],"/in0"); + sensor.VCore1 = get_float(buf,3); +/* VCore2 */ + get_file(schips.s_lm79[0],"/in1"); + sensor.VCore2 = get_float(buf,3); +/* p33V */ + get_file(schips.s_lm79[0],"/in2"); + sensor.p33V = get_float(buf,3); +/* p5V */ + get_file(schips.s_lm79[0],"/in3"); + f = get_float(buf,3); + sensor.p5V = f * ((6.80/10)+1); +/* p12V */ + get_file(schips.s_lm79[0],"/in4"); + f = get_float(buf,3); + sensor.p12V = f * ((28.00/10)+1); +/* n12V */ + get_file(schips.s_lm79[0],"/in5"); + f = get_float(buf,3); + sensor.n12V = -1 * f * (210/60.40); +/* n5V */ + get_file(schips.s_lm79[0],"/in6"); + f = get_float(buf,3); + sensor.n5V = -1 * f * (90.9/60.40); +/* vid */ + get_file(schips.s_lm79[0],"/vid"); + sensor.vid = get_float(buf,1); + } + + return sensor; +} + +lm87 get_lm87() +{ + lm87 sensor= {0,0,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01,00.01}; + + if (schips.n_lm87 > 0) { +/* fan1 */ + get_file(schips.s_lm87[0],"/fan"); + sensor.fan1 = get_int(buf,2); +/* fan2 */ + get_file(schips.s_lm87[0],"/fan2"); + sensor.fan2 = get_int(buf,2); +/* temp1 */ + get_file(schips.s_lm87[0],"/temp1"); + sensor.temp1 = get_float(buf,3); +/* CPUtemp */ + get_file(schips.s_lm87[0],"/temp2"); + sensor.CPUtemp = get_float(buf,3); +/* Vccp1 */ + get_file(schips.s_lm87[0],"/in1"); + sensor.Vccp1 = get_float(buf,3); +/* Vccp2 */ + get_file(schips.s_lm87[0],"/in5"); + sensor.Vccp2 = get_float(buf,3); +/* p25V */ + get_file(schips.s_lm87[0],"/in0"); + sensor.p25V = get_float(buf,3); +/* p33V */ + get_file(schips.s_lm87[0],"/in2"); + sensor.p33V = get_float(buf,3); +/* p5V */ + get_file(schips.s_lm87[0],"/in3"); + sensor.p5V = get_float(buf,3); +/* p12V */ + get_file(schips.s_lm87[0],"/in4"); + sensor.p12V = get_float(buf,3); +/* vid */ + get_file(schips.s_lm87[0],"/vid"); + sensor.vid = get_float(buf,1); + } + + return sensor; +} + +lm75 get_lm75() +{ + lm75 sensor= {00.01}; + + if (schips.n_lm75 > 0) { + get_file(schips.s_lm75[0],"/temp"); + sensor.temp = get_float(buf,3); + } + return sensor; +} + +void +get_chips() +{ + int i; + int n; + int nbufindex; + char *bufindex[64]; + char *temp; + + n = get_file("chips", ""); + + buf[sizeof(buf)-1] = '\0'; + + nbufindex = 0; + bufindex[nbufindex++] = &buf[0]; + for (i=0; i < n; i++) { + if (buf[i] == '\n') { + buf[i] = '\0'; + bufindex[nbufindex++] = buf + i + 1; + } + } + + for ( i=0; i < nbufindex ; i++ ) { + temp=""; + if (strncmp("lm75", bufindex[i]+4, 4) == 0 ) { + temp = strtok(bufindex[i]+4," "); + strcat(schips.s_lm75[schips.n_lm75], temp); + schips.total++; + schips.n_lm75++; + } + else if (strncmp("lm79", bufindex[i]+4, 4) == 0 ) { + temp = strtok(bufindex[i]+4," "); + strcat(schips.s_lm79[schips.n_lm79], temp); + schips.total++; + schips.n_lm79++; + } + else if (strncmp("lm87", bufindex[i]+4, 4) == 0 ) { + temp = strtok(bufindex[i]+4," "); + strcat(schips.s_lm87[schips.n_lm87], temp); + schips.total++; + schips.n_lm87++; + } + else if (strncmp("w83781d", bufindex[i]+4, 7) == 0 ) { + temp = strtok(bufindex[i]+4," "); + strcat(schips.s_w83781d[schips.n_w83781d], temp); + schips.total++; + schips.n_w83781d++; + } + else if (strncmp("mtp008", bufindex[i]+4, 6) == 0 ) { + temp = strtok(bufindex[i]+4," "); + strcat(schips.s_mtp008[schips.n_mtp008], temp); + schips.total++; + schips.n_mtp008++; + } + } +} + + +/* + * Get the contents of a file and return them + */ +int get_file(char *middle, char *end){ + + int fd; + int n; + char s[1024]="/proc/sys/dev/sensors/"; + +/* + * create the new string, the end result being the actual file name + */ + strcat(s,middle); + strcat(s,end); + +/* + * read in the file into the buffer buf + */ + if ((fd = open(s, O_RDONLY)) < 0) { + return -1; + } + n = read(fd, buf, sizeof(buf)); + close(fd); + return n; +} + +/* + * Pull a certain float value out of a string of floats + */ +float get_float(char *s, int i){ + char *temp; + float f; + int j; + + temp = strtok(s," "); + + for (j=1;jm_desc.pmid); + + if (idp->cluster > 5) + return PM_ERR_PMID; + else if (inst != PM_IN_NULL) + return PM_ERR_INST; + + if (idp->cluster == 0) { /*lmsensors*/ + switch (idp->item) { + case 0: + atom->l = schips.total; + break ; + case 1: + atom->l = schips.n_lm75; + break ; + case 2: + atom->l = schips.n_lm79; + break ; + case 3: + atom->l = schips.n_lm87; + break ; + case 4: + atom->l = schips.n_w83781d; + break ; + case 5: + atom->l = schips.n_mtp008; + break ; + default: + return PM_ERR_PMID; + } + } + if (idp->cluster == 1) { /*lmsensors.lm75*/ + if (schips.n_lm75 > 0) { + sensor75=get_lm75(); + switch (idp->item) { + case 0: + atom->f = sensor75.temp; + break ; + default: + return PM_ERR_PMID; + } + } else atom->f=9999; + } + if (idp->cluster == 2) { /*lmsensors.lm79*/ + if (schips.n_lm79 > 0) { + sensor79=get_lm79(); + switch (idp->item) { + case 0: + atom->l = sensor79.fan1; + break ; + case 1: + atom->l = sensor79.fan2; + break ; + case 2: + atom->l = sensor79.fan3; + break ; + case 3: + atom->l = sensor79.fan_div; + break ; + case 4: + atom->f = sensor79.temp; + break ; + case 5: + atom->l = sensor79.alarms; + break ; + case 6: + atom->f = sensor79.VCore1; + break ; + case 7: + atom->f = sensor79.VCore2; + break ; + case 8: + atom->f = sensor79.p33V; + break ; + case 9: + atom->f = sensor79.p5V; + break ; + case 10: + atom->f = sensor79.p12V; + break ; + case 11: + atom->f = sensor79.n12V; + break ; + case 12: + atom->f = sensor79.n5V; + break ; + case 13: + atom->f = sensor79.vid; + break ; + default: + return PM_ERR_PMID; + } + } else atom->f=9999; + } + if (idp->cluster == 3) { /*lmsensors.lm87*/ + if (schips.n_lm87 > 0) { + sensor87=get_lm87(); + switch (idp->item) { + case 0: + atom->l = sensor87.fan1; + break ; + case 1: + atom->l = sensor87.fan2; + break ; + case 2: + atom->f = sensor87.temp1; + break ; + case 3: + atom->f = sensor87.CPUtemp; + break ; + case 4: + atom->f = sensor87.Vccp1; + break ; + case 5: + atom->f = sensor87.Vccp2; + break ; + case 6: + atom->f = sensor87.p25V; + break ; + case 7: + atom->f = sensor87.p33V; + break ; + case 8: + atom->f = sensor87.p5V; + break ; + case 9: + atom->f = sensor87.p12V; + break ; + case 10: + atom->f = sensor87.vid; + break ; + default: + return PM_ERR_PMID; + } + } else atom->f=9999; + } + if (idp->cluster == 4) { /*lmsensors.w83781d*/ + if (schips.n_w83781d > 0) { + sensorw83781d=get_w83781d(); + switch (idp->item) { + case 0: + atom->l = sensorw83781d.fan1; + break ; + case 1: + atom->l = sensorw83781d.fan2; + break ; + case 2: + atom->l = sensorw83781d.fan3; + break ; + case 3: + atom->l = sensorw83781d.fan_div; + break ; + case 4: + atom->f = sensorw83781d.temp1; + break ; + case 5: + atom->f = sensorw83781d.temp2; + break ; + case 6: + atom->f = sensorw83781d.temp3; + break ; + case 7: + atom->l = sensorw83781d.alarms; + break ; + case 8: + atom->l = sensorw83781d.beep; + break ; + case 9: + atom->f = sensorw83781d.VCore1; + break ; + case 10: + atom->f = sensorw83781d.VCore2; + break ; + case 11: + atom->f = sensorw83781d.p33V; + break ; + case 12: + atom->f = sensorw83781d.p5V; + break ; + case 13: + atom->f = sensorw83781d.p12V; + break ; + case 14: + atom->f = sensorw83781d.n12V; + break ; + case 15: + atom->f = sensorw83781d.n5V; + break ; + case 16: + atom->f = sensorw83781d.vid; + break ; + default: + return PM_ERR_PMID; + } + } else atom->f=9999; + } + if (idp->cluster == 5) { /*lmsensors.mtp008*/ + if (schips.n_mtp008 > 0) { + sensormtp008=get_mtp008(); + switch (idp->item) { + case 0: + atom->l = sensormtp008.fan1; + break ; + case 1: + atom->l = sensormtp008.fan2; + break ; + case 2: + atom->l = sensormtp008.fan3; + break ; + case 3: + atom->f = sensormtp008.temp1; + break ; + case 4: + atom->f = sensormtp008.temp2; + break ; + case 5: + atom->f = sensormtp008.VCore1; + break ; + case 6: + atom->f = sensormtp008.VCore2; + break ; + case 7: + atom->f = sensormtp008.p33V; + break ; + case 8: + atom->f = sensormtp008.p12V; + break ; + case 9: + atom->f = sensormtp008.n12V; + break ; + case 10: + atom->f = sensormtp008.vid; + break ; + case 11: + atom->f = sensormtp008.vtt; + break ; + default: + return PM_ERR_PMID; + } + } else atom->f=9999; + } + + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +lmsensors_init(pmdaInterface *dp) +{ + get_chips(); + + __pmSetProcessIdentity(username); + pmdaSetFetchCallBack(dp, lmsensors_fetchCallBack); + pmdaInit(dp, NULL, 0, + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface desc; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "lmsensors" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, LMSENSORS, + "lmsensors.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&desc); + lmsensors_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + exit(0); +} diff --git a/src/pmdas/lmsensors/lmsensors.h b/src/pmdas/lmsensors/lmsensors.h new file mode 100644 index 0000000..14cbb9b --- /dev/null +++ b/src/pmdas/lmsensors/lmsensors.h @@ -0,0 +1,113 @@ +/* + * Original implementation by Troy Dawson (dawson@fnal.gov) + * + * Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +typedef struct { + int total; + int n_lm75; + int n_lm79; + int n_lm87; + int n_w83781d; + int n_mtp008; + char s_lm75[2][1024] ; + char s_lm79[2][1024] ; + char s_lm87[2][1024] ; + char s_w83781d[2][1024] ; + char s_mtp008[2][1024] ; +} chips; + +typedef struct { + float temp; +} lm75; + +typedef struct { + int fan1; + int fan2; + int fan3; + int fan_div; + float temp; + int alarms; + float VCore1; + float VCore2; + float p33V; + float p5V; + float p12V; + float n12V; + float n5V; + float vid; +} lm79; + +typedef struct { + int fan1; + int fan2; + float temp1; + float CPUtemp; + float Vccp1; + float Vccp2; + float p25V; + float p33V; + float p5V; + float p12V; + float vid; +} lm87; + +typedef struct { + int fan1; + int fan2; + int fan3; + int fan_div; + float temp1; + float temp2; + float temp3; + int alarms; + int beep; + float VCore1; + float VCore2; + float p33V; + float p5V; + float p12V; + float n12V; + float n5V; + float vid; +} w83781d; + +typedef struct { + int fan1; + int fan2; + int fan3; + float temp1; + float temp2; + float VCore1; + float VCore2; + float p33V; + float p12V; + float n12V; + float vid; + float vtt; +} mtp008; + +extern void get_chips(); +extern lm75 get_lm75(); +extern lm79 get_lm79(); +extern lm87 get_lm87(); +extern w83781d get_w83781d(); +extern mtp008 get_mtp008(); +extern int get_file(char *, char *); +extern int get_int(char * , int); +extern float get_float(char * , int); diff --git a/src/pmdas/lmsensors/pmns b/src/pmdas/lmsensors/pmns new file mode 100644 index 0000000..2a41f07 --- /dev/null +++ b/src/pmdas/lmsensors/pmns @@ -0,0 +1,106 @@ +/* + * Metrics for lmsensors PMDA + * + * Original implementation by Troy Dawson (dawson@fnal.gov) + * + * Copyright (c) 2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +lmsensors { + n_total LMSENSORS:0:0 + n_lm75 LMSENSORS:0:1 + n_lm79 LMSENSORS:0:2 + n_lm87 LMSENSORS:0:3 + n_w83781d LMSENSORS:0:4 + n_mtp008 LMSENSORS:0:5 + lm75 + lm79 + lm87 + w83781d + mtp008 +} + +lmsensors.lm75 { + temp LMSENSORS:1:0 +} + +lmsensors.lm79 { + fan1 LMSENSORS:2:0 + fan2 LMSENSORS:2:1 + fan3 LMSENSORS:2:2 + fan_div LMSENSORS:2:3 + temp LMSENSORS:2:4 + alarms LMSENSORS:2:5 + VCore1 LMSENSORS:2:6 + VCore2 LMSENSORS:2:7 + p33V LMSENSORS:2:8 + p5V LMSENSORS:2:9 + p12V LMSENSORS:2:10 + n12V LMSENSORS:2:11 + n5V LMSENSORS:2:12 + vid LMSENSORS:2:13 +} + +lmsensors.lm87 { + fan1 LMSENSORS:3:0 + fan2 LMSENSORS:3:1 + temp1 LMSENSORS:3:2 + CPUtemp LMSENSORS:3:3 + Vccp1 LMSENSORS:3:4 + Vccp2 LMSENSORS:3:5 + p25V LMSENSORS:3:6 + p33V LMSENSORS:3:7 + p5V LMSENSORS:3:8 + p12V LMSENSORS:3:9 + vid LMSENSORS:3:10 +} + +lmsensors.w83781d { + fan1 LMSENSORS:4:0 + fan2 LMSENSORS:4:1 + fan3 LMSENSORS:4:2 + fan_div LMSENSORS:4:3 + temp1 LMSENSORS:4:4 + temp2 LMSENSORS:4:5 + temp3 LMSENSORS:4:6 + alarms LMSENSORS:4:7 + beep LMSENSORS:4:8 + VCore1 LMSENSORS:4:9 + VCore2 LMSENSORS:4:10 + p33V LMSENSORS:4:11 + p5V LMSENSORS:4:12 + p12V LMSENSORS:4:13 + n12V LMSENSORS:4:14 + n5V LMSENSORS:4:15 + vid LMSENSORS:4:16 +} + +lmsensors.mtp008 { + fan1 LMSENSORS:5:0 + fan2 LMSENSORS:5:1 + fan3 LMSENSORS:5:2 + temp1 LMSENSORS:5:3 + temp2 LMSENSORS:5:4 + VCore1 LMSENSORS:5:5 + VCore2 LMSENSORS:5:6 + p33V LMSENSORS:5:7 + p12V LMSENSORS:5:8 + n12V LMSENSORS:5:9 + vid LMSENSORS:5:10 + vtt LMSENSORS:5:11 +} + diff --git a/src/pmdas/lmsensors/root b/src/pmdas/lmsensors/root new file mode 100644 index 0000000..b17d559 --- /dev/null +++ b/src/pmdas/lmsensors/root @@ -0,0 +1,12 @@ +/* + * fake "root" for validating the local PMNS subtree + * + * Original implementation by Troy Dawson (dawson@fnal.gov) + */ + +#include + +root { lmsensors } + +#include "pmns" + diff --git a/src/pmdas/logger/GNUmakefile b/src/pmdas/logger/GNUmakefile new file mode 100644 index 0000000..e5b52f1 --- /dev/null +++ b/src/pmdas/logger/GNUmakefile @@ -0,0 +1,56 @@ +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2011 Nathan Scott. All Rights Reversed. +# Copyright (c) 2011-2012 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +CMDTARGET = pmdalogger$(EXECSUFFIX) +DFILES = README +CFILES = event.c util.c logger.c +HFILES = event.h util.h +LLDLIBS = $(PCP_PMDALIB) +LSRCFILES = Install Remove pmns help $(DFILES) root + +IAM = logger +DOMAIN = LOGGER +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h *.o $(IAM).log $(CMDTARGET) + +default: domain.h $(CMDTARGET) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns $(PMDADIR) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/$(CMDTARGET) + +logger.o: domain.h +event.o logger.o: event.h +util.o event.o logger.o: util.h + +.NOTPARALLEL: +.ORDER: domain.h $(OBJECTS) + +default_pcp : default + +install_pcp : install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/logger/Install b/src/pmdas/logger/Install new file mode 100644 index 0000000..10deb91 --- /dev/null +++ b/src/pmdas/logger/Install @@ -0,0 +1,164 @@ +#! /bin/sh +# +# Copyright (c) 2011-2012 Red Hat. +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the logger PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=logger +pmda_interface=5 +forced_restart=false + +pmdaSetup + +# be careful that mortals cannot write any configuration files, as +# these would present a security problem +# +umask 022 + +# PMDA variables +# +configfile="" + +_parsedefaults() +{ + echo "Extracting options from current installation ..." + while getopts D:d:l c + do + case $c in + \?) echo "Warning: Unrecognized option in $PCP_PMCDCONF_PATH" + echo " Remove line for the $iam PMDA in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2;; + * ) ;; + esac + done + eval configfile='$'$OPTIND +} + +# Get logfile(s) to monitor +if $do_pmda +then + # set options from $PCP_PMCDCONF_PATH, if possible + # + ans=`$PCP_AWK_PROG <$PCP_PMCDCONF_PATH ' +$1 == "'$iam'" { printf "%s",$6 + for (i=7;i<=NF;i++) printf " %s",$i + print "" + }'` + if [ ! -z "$ans" ] + then + _parsedefaults $ans + fi + + # go figure out which configuration file to use ... + # + #default_configfile=./sample.conf + default_configfile='' + pmdaChooseConfigFile + if [ ! -f "$configfile" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to enter logfile names and paths manually? [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = "Xy" -o "X$ans" = "XY" -o -z "$ans" ] + then + configfile="$configdir/$iam.conf" + if [ -f "$configfile" ] + then + echo "Removing old configuration file \"$configfile\"" + rm -f "$configfile" + if [ -f "$configfile" ] + then + echo "Cannot remove \"$configfile\"" + exit 1 + fi + fi + + echo + echo \ +'Enter the PMNS name and logfile path. If the path ends in "|", the +filename is interpreted as a command which will output data. + +An empty line terminates the logfile selection process and there must +be at least one logfile specified. ' + + args="" + touch "$configfile" + if [ ! -f "$configfile" ] + then + echo "Installation aborted." + exit 1 + fi + + while [ ! -s "$configfile" ] + do + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N "Logfile PMNS name: ""$PCP_ECHO_C" + read name + [ -z "$name" ] && break + + # Check name for invalid chars. + if ! echo $name | grep "^[A-Za-z][_A-Za-z0-9]*$" > /dev/null + then + echo \ +"Invalid characters in PMNS name: \"$name\". +Names must start with an alphabetic character ([a-zA-Z]). The rest of +the characters in the name must be alphanumeric ([a-zA-Z0-9]) or an +underscore ('_')." + continue + fi + + # Make sure name isn't already in the logfile. + if grep "^${name}[ \t]" "$configfile" >/dev/null + then + echo "Sorry, logfile PMNS name \"$name\" already specified. Please try again." + continue + fi + + $PCP_ECHO_PROG $PCP_ECHO_N "Logfile pathname: ""$PCP_ECHO_C" + read pathname + [ -z "$pathname" ] && break + if grep "[ \t]${pathname}$" "$configfile" >/dev/null + then + echo "Sorry, pathname \"$pathname\" already specified. Please try again." + continue + fi + + $PCP_ECHO_PROG $PCP_ECHO_N "Restricted access [n]: ""$PCP_ECHO_C" + read restrict + if [ "$restrict" = "y" -o "$restrict" = "yes" ]; then + restrict="y" + else + restrict="n" + fi + + echo "$name $restrict $pathname" >>"$configfile" + done + done + else + echo "" + echo "Error: Abandoning installation as no configuration file was specified." + exit 1 + fi + fi + + args="$configfile" +fi + +pmdaInstall + +exit 0 diff --git a/src/pmdas/logger/README b/src/pmdas/logger/README new file mode 100644 index 0000000..a19eeaf --- /dev/null +++ b/src/pmdas/logger/README @@ -0,0 +1,51 @@ +Logger PMDA +=========== + +This PMDA exports information about the event status of log files +specified in a config file. The default configuration file is +$PCP_VAR_DIR/config/logger/logger.conf, which should contain one +line for each file you wish to monitor. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT logger + +Installation +============ + + + # cd $PCP_PMDAS_DIR/logger + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/logger + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/logger.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/logger/Remove b/src/pmdas/logger/Remove new file mode 100644 index 0000000..6c014ef --- /dev/null +++ b/src/pmdas/logger/Remove @@ -0,0 +1,24 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2011 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the logger PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh +iam=logger +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/logger/event.c b/src/pmdas/logger/event.c new file mode 100644 index 0000000..19ffda9 --- /dev/null +++ b/src/pmdas/logger/event.c @@ -0,0 +1,493 @@ +/* + * Event support for the Logger PMDA + * + * Copyright (c) 2011-2012 Red Hat. + * Copyright (c) 2011 Nathan Scott. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "event.h" +#include "pmda.h" +#include "util.h" +#include +#ifdef HAVE_REGEX_H +#include +#endif + +static int numlogfiles; +static event_logfile_t *logfiles; + +void +event_init(pmID pmid) +{ + char cmd[MAXPATHLEN]; + int i, fd; + + for (i = 0; i < numlogfiles; i++) { + size_t pathlen = strlen(logfiles[i].pathname); + + /* + * We support 2 kinds of PATHNAMEs: + * (1) Regular paths. These paths are opened normally. + * (2) Pipes. If the path ends in '|', the filename is + * interpreted as a command which pipes input to us. + */ + if (logfiles[i].pathname[pathlen - 1] != '|') { + fd = open(logfiles[i].pathname, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + if (logfiles[i].fd >= 0) /* log once only */ + __pmNotifyErr(LOG_ERR, "open: %s - %s", + logfiles[i].pathname, strerror(errno)); + } else { + if (fstat(fd, &logfiles[i].pathstat) < 0) + if (logfiles[i].fd >= 0) /* log once only */ + __pmNotifyErr(LOG_ERR, "fstat: %s - %s", + logfiles[i].pathname, strerror(errno)); + lseek(fd, 0, SEEK_END); + } + } + else { + strncpy(cmd, logfiles[i].pathname, sizeof(cmd)); + cmd[pathlen - 1] = '\0'; /* get rid of the '|' */ + rstrip(cmd); /* Remove all trailing whitespace. */ + fd = start_cmd(cmd, &logfiles[i].pid); + if (fd < 0) { + if (logfiles[i].fd >= 0) /* log once only */ + __pmNotifyErr(LOG_ERR, "pipe: %s - %s", + logfiles[i].pathname, strerror(errno)); + } else { + if (fd > maxfd) + maxfd = fd; + FD_SET(fd, &fds); + } + } + + logfiles[i].fd = fd; /* keep file descriptor (or error) */ + logfiles[i].pmid = pmid; /* string param metric identifier */ + logfiles[i].queueid = pmdaEventNewQueue(logfiles[i].pmnsname, maxmem); + } +} + +void +event_shutdown(void) +{ + int i; + + __pmNotifyErr(LOG_INFO, "%s: Shutting down...", __FUNCTION__); + + for (i = 0; i < numlogfiles; i++) { + if (logfiles[i].pid != 0) { + stop_cmd(logfiles[i].pid); + logfiles[i].pid = 0; + } + if (logfiles[i].fd > 0) { + close(logfiles[i].fd); + logfiles[i].fd = 0; + } + } +} + +/* + * Ensure given name (identifier) can be used as a namespace entry. + */ +static int +valid_pmns_name(char *name) +{ + if (!isalpha((int)name[0])) + return 0; + for (; *name != '\0'; name++) + if (!isalnum((int)*name) && *name != '_') + return 0; + return 1; +} + +/* + * Parse the configuration file and do initial data structure setup. + */ +int +event_config(const char *fname) +{ + FILE *configFile; + event_logfile_t *logfile; + int sts = 0; + size_t len; + char line[MAXPATHLEN * 2]; + char *ptr, *name, *noaccess; + + configFile = fopen(fname, "r"); + if (configFile == NULL) { + __pmNotifyErr(LOG_ERR, "event_config: %s: %s", fname, strerror(errno)); + return -1; + } + + while (!feof(configFile)) { + if (fgets(line, sizeof(line), configFile) == NULL) { + if (feof(configFile)) + break; + __pmNotifyErr(LOG_ERR, "event_config: fgets: %s", strerror(errno)); + sts = -1; + break; + } + + /* + * fgets() puts the '\n' at the end of the buffer. Remove + * it. If it isn't there, that must mean that the line is + * longer than our buffer. + */ + len = strlen(line); + if (len == 0) /* Ignore empty strings. */ + continue; + if (line[len - 1] != '\n') { /* String must be too long */ + __pmNotifyErr(LOG_ERR, "event_config: config line too long: '%s'", + line); + sts = -1; + break; + } + line[len - 1] = '\0'; /* Remove the '\n'. */ + + /* Strip all trailing whitespace. */ + rstrip(line); + + /* If the string is now empty or a comment, just ignore the line. */ + len = strlen(line); + if (len == 0) + continue; + if (line[0] == '#') + continue; + + /* Skip past all leading whitespace to find the start of + * NAME. */ + ptr = name = lstrip(line); + + /* Now we need to split the line into 3 parts: NAME, ACCESS + * and PATHNAME. NAME can't have whitespace in it, so look + * for the first non-whitespace. */ + while (*ptr != '\0' && ! isspace((int)*ptr)) { + ptr++; + } + /* If we're at the end, we didn't find any whitespace, so + * we've only got a NAME, with no ACCESS/PATHNAME. */ + if (*ptr == '\0') { + __pmNotifyErr(LOG_ERR, "event_config: badly formatted " + " configuration file line: '%s'", line); + sts = -1; + break; + } + /* Terminate NAME at the 1st whitespace. */ + *ptr++ = '\0'; + + /* Make sure NAME isn't too long. */ + if (strlen(name) > MAXPATHLEN) { + __pmNotifyErr(LOG_ERR, "event_config: name too long: '%s'", name); + sts = -1; + break; + } + + /* Make sure NAME is valid. */ + if (valid_pmns_name(name) == 0) { + __pmNotifyErr(LOG_ERR, "event_config: invalid name: '%s'", name); + sts = -1; + break; + } + + /* Skip past any extra whitespace between NAME and ACCESS */ + ptr = noaccess = lstrip(ptr); + + /* Look for the next whitespace, and that terminate ACCESS */ + while (*ptr != '\0' && ! isspace((int)*ptr)) { + ptr++; + } + + /* If we're at the end, we didn't find any whitespace, so + * we've only got NAME and ACCESS with no/PATHNAME. */ + if (*ptr == '\0') { + __pmNotifyErr(LOG_ERR, "event_config: badly formatted " + " configuration file line: '%s'", line); + sts = -1; + break; + } + /* Terminate ACCESS at the 1st whitespace. */ + *ptr++ = '\0'; + + /* Skip past any extra whitespace between ACCESS and PATHNAME */ + ptr = lstrip(ptr); + + /* Make sure PATHNAME (the rest of the line) isn't too long. */ + if (strlen(ptr) > MAXPATHLEN) { + __pmNotifyErr(LOG_ERR, "event_config: path is too long: '%s'", ptr); + sts = -1; + break; + } + + /* Now we've got a reasonable NAME/ACCESS/PATHNAME. Save them. */ + len = (numlogfiles + 1) * sizeof(event_logfile_t); + logfiles = realloc(logfiles, len); + if (logfiles == NULL) { + __pmNoMem("event_config", len, PM_FATAL_ERR); + sts = -1; + break; + } + logfile = &logfiles[numlogfiles]; + memset(logfile, 0, sizeof(*logfile)); + logfile->noaccess = (noaccess[0] == 'y' || noaccess[0] == 'Y'); + strncpy(logfile->pmnsname, name, sizeof(logfile->pmnsname)); + logfile->pmnsname[sizeof(logfile->pmnsname)-1] = '\0'; + strncpy(logfile->pathname, ptr, sizeof(logfile->pathname)); + logfile->pathname[sizeof(logfile->pathname)-1] = '\0'; + /* remaining fields filled in after pmdaInit() is called. */ + numlogfiles++; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_INFO, "event_config: new logfile %s (%s)", + logfile->pathname, logfile->pmnsname); + } + + fclose(configFile); + if (sts < 0) { + free(logfiles); + return sts; + } + if (numlogfiles == 0) { + __pmNotifyErr(LOG_ERR, "event_config: no valid log files found"); + return -1; + } + return numlogfiles; +} + +static int +event_create(event_logfile_t *logfile) +{ + int j; + char *s, *p; + size_t offset; + ssize_t bytes; + struct timeval timestamp; + + static char *buffer; + static int bufsize; + + /* + * Using a static (global) event buffer to hold initial read. + * The aim is to reduce memory allocation until we know we'll + * need to keep something. + */ + if (!buffer) { + int sts = 0; + bufsize = 16 * getpagesize(); +#ifdef HAVE_POSIX_MEMALIGN + sts = posix_memalign((void **)&buffer, getpagesize(), bufsize); +#else +#ifdef HAVE_MEMALIGN + buffer = (char *)memalign(getpagesize(), bufsize); + if (buffer == NULL) sts = -1; +#else + buffer = (char *)malloc(bufsize); + if (buffer == NULL) sts = -1; +#endif +#endif + if (sts != 0) { + __pmNotifyErr(LOG_ERR, "event buffer allocation failure"); + return -1; + } + } + + offset = 0; +multiread: + if (logfile->fd < 0) + return 0; + bytes = read(logfile->fd, buffer + offset, bufsize - 1 - offset); + /* + * Ignore the error if: + * - we've got EOF (0 bytes read) + * - EBADF (fd isn't valid - most likely a closed pipe) + * - EAGAIN/EWOULDBLOCK (fd is marked nonblocking and read would block) + * - EINVAL/EISDIR (fd is a directory - config file botch) + */ + if (bytes == 0) + return 0; + if (bytes < 0 && (errno == EBADF || errno == EISDIR || errno == EINVAL)) + return 0; + if (bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return 0; + if (bytes > maxmem) + return 0; + if (bytes < 0) { + __pmNotifyErr(LOG_ERR, "read failure on %s: %s", + logfile->pathname, strerror(errno)); + return -1; + } + + gettimeofday(×tamp, NULL); + buffer[bufsize-1] = '\0'; + for (s = p = buffer, j = 0; *s != '\0' && j < bufsize-1; s++, j++) { + if (*s != '\n') + continue; + *s = '\0'; + bytes = (s+1) - p; + pmdaEventQueueAppend(logfile->queueid, p, bytes, ×tamp); + p = s + 1; + } + /* did we just do a full buffer read? */ + if (p == buffer) { + char msg[64]; + __pmNotifyErr(LOG_ERR, "Ignoring long (%d bytes) line: \"%s\"", (int) + bytes, __pmdaEventPrint(p, bytes, msg, sizeof(msg))); + } else if (j == bufsize - 1) { + offset = bufsize-1 - (p - buffer); + memmove(buffer, p, offset); + goto multiread; /* read rest of line */ + } + return 1; +} + +void +event_refresh(void) +{ + struct event_logfile *logfile; + struct stat pathstat; + int i, fd, sts; + + for (i = 0; i < numlogfiles; i++) { + logfile = &logfiles[i]; + + if (logfile->pid > 0) /* process pipe */ + goto events; + if (stat(logfile->pathname, &pathstat) < 0) { + if (logfile->fd >= 0) { + close(logfile->fd); + logfile->fd = -1; + } + memset(&logfile->pathstat, 0, sizeof(logfile->pathstat)); + } else { + /* reopen if no descriptor before, or log rotated (new file) */ + if (logfile->fd < 0 || + logfile->pathstat.st_ino != pathstat.st_ino || + logfile->pathstat.st_dev != pathstat.st_dev) { + if (logfile->fd >= 0) + close(logfile->fd); + fd = open(logfile->pathname, O_RDONLY|O_NONBLOCK); + if (fd < 0 && logfile->fd >= 0) /* log once */ + __pmNotifyErr(LOG_ERR, "open: %s - %s", + logfile->pathname, strerror(errno)); + logfile->fd = fd; + } else { + if ((S_ISREG(pathstat.st_mode)) && + (memcmp(&logfile->pathstat.st_mtime, &pathstat.st_mtime, + sizeof(pathstat.st_mtime))) == 0) + continue; + } + logfile->pathstat = pathstat; +events: + do { + sts = event_create(logfile); + } while (sts != 0); + } + } +} + +int +event_logcount(void) +{ + return numlogfiles; +} + +int +event_queueid(int handle) +{ + if (handle < 0 || handle >= numlogfiles) + return 0; + + /* if logfile unrestricted, allow this client access to this queue */ + if (logfiles[handle].noaccess == 0) + pmdaEventSetAccess(pmdaGetContext(), logfiles[handle].queueid, 1); + + return logfiles[handle].queueid; +} + +__uint64_t +event_pathsize(int handle) +{ + if (handle < 0 || handle >= numlogfiles) + return 0; + return logfiles[handle].pathstat.st_size; +} + +const char * +event_pathname(int handle) +{ + if (handle < 0 || handle >= numlogfiles) + return NULL; + return logfiles[handle].pathname; +} + +const char * +event_pmnsname(int handle) +{ + if (handle < 0 || handle >= numlogfiles) + return NULL; + return logfiles[handle].pmnsname; +} + +pmID +event_pmid(int handle) +{ + if (handle < 0 || handle >= numlogfiles) + return 0; + return logfiles[handle].pmid; +} + +int +event_decoder(int eventarray, void *buffer, size_t size, + struct timeval *timestamp, void *data) +{ + int sts, handle = *(int *)data; + pmID pmid = event_pmid(handle); + pmAtomValue atom; + + sts = pmdaEventAddRecord(eventarray, timestamp, PM_EVENT_FLAG_POINT); + if (sts < 0) + return sts; + atom.cp = buffer; + sts = pmdaEventAddParam(eventarray, pmid, PM_TYPE_STRING, &atom); + if (sts < 0) + return sts; + return 1; /* simple decoder, added just one event array */ +} + +int +event_regex_apply(void *rp, void *data, size_t size) +{ + regex_t *regex = (regex_t *)rp; + return regexec(regex, data, 0, NULL, 0) == REG_NOMATCH; +} + +void +event_regex_release(void *rp) +{ + regex_t *regex = (regex_t *)rp; + regfree(regex); +} + +int +event_regex_alloc(const char *string, void **filter) +{ + regex_t *regex = malloc(sizeof(regex_t)); + + if (regex == NULL) + return -ENOMEM; + if (regcomp(regex, string, REG_EXTENDED|REG_NOSUB) != 0) { + free(regex); + return PM_ERR_CONV; + } + *filter = (void *)regex; + return 0; +} diff --git a/src/pmdas/logger/event.h b/src/pmdas/logger/event.h new file mode 100644 index 0000000..b1f176f --- /dev/null +++ b/src/pmdas/logger/event.h @@ -0,0 +1,56 @@ +/* + * Event support for the Logger PMDA + * + * Copyright (c) 2011 Red Hat Inc. + * Copyright (c) 2011 Nathan Scott. All rights reversed. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _EVENT_H +#define _EVENT_H + +#include "pmapi.h" +#include "impl.h" +#include + +typedef struct event_logfile { + pmID pmid; + int fd; + pid_t pid; + int queueid; + int noaccess; + struct stat pathstat; + char pmnsname[MAXPATHLEN]; + char pathname[MAXPATHLEN]; +} event_logfile_t; + +extern int maxfd; +extern fd_set fds; +extern long maxmem; + +extern void event_init(pmID pmid); +extern void event_shutdown(void); +extern void event_refresh(void); +extern int event_config(const char *filename); + +extern int event_logcount(void); +extern pmID event_pmid(int handle); +extern int event_queueid(int handle); +extern __uint64_t event_pathsize(int handle); +extern const char *event_pathname(int handle); +extern const char *event_pmnsname(int handle); +extern int event_decoder(int arrayid, void *buffer, size_t size, + struct timeval *timestamp, void *data); +extern int event_regex_alloc(const char *s, void **filter); +extern int event_regex_apply(void *rp, void *data, size_t size); +extern void event_regex_release(void *rp); + +#endif /* _EVENT_H */ diff --git a/src/pmdas/logger/help b/src/pmdas/logger/help new file mode 100644 index 0000000..b7c1c6c --- /dev/null +++ b/src/pmdas/logger/help @@ -0,0 +1,37 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# logger PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ logger.numclients The number of attached clients +The number of attached clients. +@ logger.numlogfiles The number of monitored logfiles +The number of monitored logfiles. +@ logger.param_string String event data +String event data. +@ logger.maxmem Maximum number of queued event bytes +Maximum number of queued event bytes for each log file. diff --git a/src/pmdas/logger/logger.c b/src/pmdas/logger/logger.c new file mode 100644 index 0000000..98689d7 --- /dev/null +++ b/src/pmdas/logger/logger.c @@ -0,0 +1,587 @@ +/* + * Logger, a configurable log file monitoring PMDA + * + * Copyright (c) 2011-2012 Red Hat. + * Copyright (c) 2011 Nathan Scott. All Rights Reserved. + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * Debug options + * APPL0 configfile processing and PMNS setup + * APPL1 loading event data from the log files + * APPL2 interaction with PMCD + */ + +#include "domain.h" +#include "event.h" +#include "util.h" +#include "pmda.h" + +/* + * Logger PMDA + * + * Metrics + * logger.numclients - number of attached clients + * logger.numlogfiles - number of monitored logfiles + * logger.param_string - string event data + * logger.perfile.{LOGFILE}.count - observed event count + * logger.perfile.{LOGFILE}.bytes - observed events size + * logger.perfile.{LOGFILE}.size - logfile size + * logger.perfile.{LOGFILE}.path - logfile path + * logger.perfile.{LOGFILE}.numclients - number of attached + * clients/logfile + * logger.perfile.{LOGFILE}.records - event records/logfile + */ + +#define DEFAULT_MAXMEM (2 * 1024 * 1024) /* 2 megabytes */ +long maxmem; + +int maxfd; +fd_set fds; +static int interval_expired; +static struct timeval interval = { 2, 0 }; +static char *username; + +static int nummetrics; +static __pmnsTree *pmns; + +typedef struct dynamic_metric_info { + int handle; + int pmid_index; + const char *help_text; +} dynamic_metric_info_t; +static dynamic_metric_info_t *dynamic_metric_infotab; + +static pmdaMetric dynamic_metrictab[] = { +/* perfile.{LOGFILE}.count */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* perfile.{LOGFILE}.bytes */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* perfile.{LOGFILE}.size */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* perfile.{LOGFILE}.path */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* perfile.{LOGFILE}.numclients */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* perfile.{LOGFILE}.records */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_EVENT, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* perfile.{LOGFILE}.queuemem */ + { NULL, /* m_user gets filled in later */ + { 0 /* pmid gets filled in later */, PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +}; + +static char *dynamic_nametab[] = { +/* perfile.{LOGFILE}.count */ + "count", +/* perfile.{LOGFILE}.bytes */ + "bytes", +/* perfile.{LOGFILE}.size */ + "size", +/* perfile.{LOGFILE}.path */ + "path", +/* perfile.{LOGFILE}.numclients */ + "numclients", +/* perfile.{LOGFILE}.records */ + "records", +/* perfile.{LOGFILE}.queuemem */ + "queuemem", +}; + +static const char *dynamic_helptab[] = { +/* perfile.{LOGFILE}.count */ + "The cumulative number of events seen for this logfile.", +/* perfile.{LOGFILE}.bytes */ + "Cumulative number of bytes in events seen for this logfile.", +/* perfile.{LOGFILE}.size */ + "The current size of this logfile.", +/* perfile.{LOGFILE}.path */ + "The path for this logfile.", +/* perfile.{LOGFILE}.numclients */ + "The number of attached clients for this logfile.", +/* perfile.{LOGFILE}.records */ + "Event records for this logfile.", +/* perfile.{LOGFILE}.queuemem */ + "Amount of memory used for event data.", +}; + +static pmdaMetric static_metrictab[] = { +/* numclients */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* numlogfiles */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* param_string */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* perfile.maxmem */ + { NULL, /* m_user gets filled in later */ + { PMDA_PMID(0,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +}; + +static pmdaMetric *metrictab; + +static int +logger_profile(__pmProfile *prof, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return 0; +} + +static int +logger_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +valid_pmid(unsigned int cluster, unsigned int item) +{ + if (cluster != 0 || item > nummetrics) + return PM_ERR_PMID; + return 0; +} + +static int +logger_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int sts; + + if ((sts = valid_pmid(idp->cluster, idp->item)) < 0) + return sts; + + sts = PMDA_FETCH_STATIC; + if (idp->item < 4) { + switch (idp->item) { + case 0: /* logger.numclients */ + sts = pmdaEventClients(atom); + break; + case 1: /* logger.numlogfiles */ + atom->ul = event_logcount(); + break; + case 2: /* logger.param_string */ + sts = PMDA_FETCH_NOVALUES; + break; + case 3: /* logger.maxmem */ + atom->ull = (unsigned long long)maxmem; + break; + default: + return PM_ERR_PMID; + } + } + else { + dynamic_metric_info_t *pinfo; + int queue; + + if ((pinfo = ((mdesc != NULL) ? mdesc->m_user : NULL)) == NULL) + return PM_ERR_PMID; + queue = event_queueid(pinfo->handle); + + switch (pinfo->pmid_index) { + case 0: /* perfile.{LOGFILE}.count */ + sts = pmdaEventQueueCounter(queue, atom); + break; + case 1: /* perfile.{LOGFILE}.bytes */ + sts = pmdaEventQueueBytes(queue, atom); + break; + case 2: /* perfile.{LOGFILE}.size */ + atom->ull = event_pathsize(pinfo->handle); + break; + case 3: /* perfile.{LOGFILE}.path */ + atom->cp = (char *)event_pathname(pinfo->handle); + break; + case 4: /* perfile.{LOGFILE}.numclients */ + sts = pmdaEventQueueClients(queue, atom); + break; + case 5: /* perfile.{LOGFILE}.records */ + sts = pmdaEventQueueRecords(queue, atom, pmdaGetContext(), + event_decoder, &pinfo->handle); + break; + case 6: /* perfile.{LOGFILE}.queuemem */ + sts = pmdaEventQueueMemory(queue, atom); + break; + default: + return PM_ERR_PMID; + } + } + return sts; +} + +static int +logger_store(pmResult *result, pmdaExt *pmda) +{ + int i, j, sts; + + pmdaEventNewClient(pmda->e_context); + + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + __pmID_int *idp = (__pmID_int *)&vsp->pmid; + dynamic_metric_info_t *pinfo = NULL; + void *filter; + int queueid; + + if ((sts = valid_pmid(idp->cluster, idp->item)) < 0) + return sts; + for (j = 0; j < pmda->e_nmetrics; j++) { + if (vsp->pmid == pmda->e_metrics[j].m_desc.pmid) { + pinfo = pmda->e_metrics[j].m_user; + break; + } + } + if (pinfo == NULL) + return PM_ERR_PMID; + if (pinfo->pmid_index != 5) + return PM_ERR_PERMISSION; + queueid = event_queueid(pinfo->handle); + + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_SPTR) + return PM_ERR_CONV; + + sts = event_regex_alloc(vsp->vlist[0].value.pval->vbuf, &filter); + if (sts < 0) + return sts; + + sts = pmdaEventSetFilter(pmda->e_context, queueid, filter, + event_regex_apply, event_regex_release); + if (sts < 0 ) + return sts; + } + return 0; +} + +static void +logger_end_contextCallBack(int context) +{ + pmdaEventEndClient(context); +} + +static int +logger_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaTreePMID(pmns, name, pmid); +} + +static int +logger_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaTreeName(pmns, pmid, nameset); +} + +static int +logger_children(const char *name, int traverse, char ***kids, int **sts, + pmdaExt *pmda) +{ + pmdaEventNewClient(pmda->e_context); + return pmdaTreeChildren(pmns, name, traverse, kids, sts); +} + +static int +logger_text(int ident, int type, char **buffer, pmdaExt *pmda) +{ + int numstatics = sizeof(static_metrictab)/sizeof(static_metrictab[0]); + + pmdaEventNewClient(pmda->e_context); + + if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) { + /* Lookup pmid in the metric table. */ + int item = pmid_item(ident); + + /* If the PMID item was for a dynamic metric... */ + if (item >= numstatics && item < nummetrics + /* and the PMID matches... */ + && metrictab[item].m_desc.pmid == (pmID)ident + /* and we've got user data... */ + && metrictab[item].m_user != NULL) { + dynamic_metric_info_t *pinfo = metrictab[item].m_user; + + /* Return the correct help text. */ + *buffer = (char *)pinfo->help_text; + return 0; + } + } + return pmdaText(ident, type, buffer, pmda); +} + +void +logger_init(pmdaInterface *dp, const char *configfile) +{ + size_t size; + int i, j, sts, item, numloggers; + int numstatics = sizeof(static_metrictab)/sizeof(static_metrictab[0]); + int numdynamics = sizeof(dynamic_metrictab)/sizeof(dynamic_metrictab[0]); + pmdaMetric *pmetric; + char name[MAXPATHLEN * 2]; + dynamic_metric_info_t *pinfo; + + __pmSetProcessIdentity(username); + + /* Read and parse config file. */ + if ((numloggers = event_config(configfile)) < 0) + return; + + /* Create the dynamic metric info table based on the logfile table */ + size = sizeof(struct dynamic_metric_info) * numdynamics * numloggers; + if ((dynamic_metric_infotab = malloc(size)) == NULL) { + __pmNoMem("logger_init(dynamic)", size, PM_FATAL_ERR); + return; + } + pinfo = dynamic_metric_infotab; + for (i = 0; i < numloggers; i++) { + for (j = 0; j < numdynamics; j++) { + pinfo->handle = i; + pinfo->pmid_index = j; + pinfo->help_text = dynamic_helptab[j]; + pinfo++; + } + } + + /* Create the metric table based on the static and dynamic metric tables */ + nummetrics = numstatics + (numloggers * numdynamics); + size = sizeof(pmdaMetric) * nummetrics; + if ((metrictab = malloc(size)) == NULL) { + free(dynamic_metric_infotab); + __pmNoMem("logger_init(static)", size, PM_FATAL_ERR); + return; + } + memcpy(metrictab, static_metrictab, sizeof(static_metrictab)); + pmetric = &metrictab[numstatics]; + pinfo = dynamic_metric_infotab; + item = numstatics; + for (i = 0; i < numloggers; i++) { + memcpy(pmetric, dynamic_metrictab, sizeof(dynamic_metrictab)); + for (j = 0; j < numdynamics; j++) { + pmetric[j].m_desc.pmid = PMDA_PMID(0, item++); + pmetric[j].m_user = pinfo++; + } + pmetric += numdynamics; + } + + if (dp->status != 0) + return; + + dp->version.four.fetch = logger_fetch; + dp->version.four.store = logger_store; + dp->version.four.profile = logger_profile; + dp->version.four.pmid = logger_pmid; + dp->version.four.name = logger_name; + dp->version.four.children = logger_children; + dp->version.four.text = logger_text; + + pmdaSetFetchCallBack(dp, logger_fetchCallBack); + pmdaSetEndContextCallBack(dp, logger_end_contextCallBack); + + pmdaInit(dp, NULL, 0, metrictab, nummetrics); + + /* Create the dynamic PMNS tree and populate it. */ + if ((sts = __pmNewPMNS(&pmns)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create new pmns: %s\n", + pmProgname, pmErrStr(sts)); + pmns = NULL; + return; + } + pmetric = &metrictab[numstatics]; + for (i = 0; i < numloggers; i++) { + const char *id = event_pmnsname(i); + for (j = 0; j < numdynamics; j++) { + snprintf(name, sizeof(name), + "logger.perfile.%s.%s", id, dynamic_nametab[j]); + __pmAddPMNSNode(pmns, pmetric[j].m_desc.pmid, name); + } + pmetric += numdynamics; + } + /* for reverse (pmid->name) lookups */ + pmdaTreeRebuildHash(pmns, (numloggers * numdynamics)); + + /* initialise the event and client tracking code */ + event_init(metrictab[2].m_desc.pmid); +} + +static void +logger_timer(int sig, void *ptr) +{ + interval_expired = 1; +} + +void +loggerMain(pmdaInterface *dispatch) +{ + fd_set readyfds; + int nready, pmcdfd; + + pmcdfd = __pmdaInFd(dispatch); + if (pmcdfd > maxfd) + maxfd = pmcdfd; + + FD_ZERO(&fds); + FD_SET(pmcdfd, &fds); + + /* arm interval timer */ + if (__pmAFregister(&interval, NULL, logger_timer) < 0) { + __pmNotifyErr(LOG_ERR, "registering event interval handler"); + exit(1); + } + + for (;;) { + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, NULL); + if (pmDebug & DBG_TRACE_APPL2) + __pmNotifyErr(LOG_DEBUG, "select: nready=%d interval=%d", + nready, interval_expired); + if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } else if (!interval_expired) { + continue; + } + } + + __pmAFblock(); + if (nready > 0 && FD_ISSET(pmcdfd, &readyfds)) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd PDU [fd=%d]", pmcdfd); + if (__pmdaMainPDU(dispatch) < 0) { + __pmAFunblock(); + exit(1); /* fatal if we lose pmcd */ + } + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "completed pmcd PDU [fd=%d]", pmcdfd); + } + if (interval_expired) { + interval_expired = 0; + event_refresh(); + } + __pmAFunblock(); + } +} + +static void +convertUnits(char **endnum, long *maxmem) +{ + switch ((int) **endnum) { + case 'b': + case 'B': + break; + case 'k': + case 'K': + *maxmem *= 1024; + break; + case 'm': + case 'M': + *maxmem *= 1024 * 1024; + break; + case 'g': + case 'G': + *maxmem *= 1024 * 1024 * 1024; + break; + } + (*endnum)++; +} + +static void +usage(void) +{ + fprintf(stderr, + "Usage: %s [options] configfile\n\n" + "Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than the default\n" + " -m memory maximum memory used per logfile (default %ld bytes)\n" + " -s interval default delay between iterations (default %d sec)\n" + " -U username user account to run under (default \"pcp\")\n", + pmProgname, maxmem, (int)interval.tv_sec); + exit(1); +} + +int +main(int argc, char **argv) +{ + static char helppath[MAXPATHLEN]; + char *endnum; + pmdaInterface desc; + long minmem; + int c, err = 0, sep = __pmPathSeparator(); + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + minmem = getpagesize(); + maxmem = (minmem > DEFAULT_MAXMEM) ? minmem : DEFAULT_MAXMEM; + snprintf(helppath, sizeof(helppath), "%s%c" "logger" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_5, pmProgname, LOGGER, + "logger.log", helppath); + + while ((c = pmdaGetOpt(argc, argv, "D:d:l:m:s:U:?", &desc, &err)) != EOF) { + switch (c) { + case 'm': + maxmem = strtol(optarg, &endnum, 10); + if (*endnum != '\0') + convertUnits(&endnum, &maxmem); + if (*endnum != '\0' || maxmem < minmem) { + fprintf(stderr, "%s: invalid max memory '%s' (min=%ld)\n", + pmProgname, optarg, minmem); + err++; + } + break; + + case 's': + if (pmParseInterval(optarg, &interval, &endnum) < 0) { + fprintf(stderr, "%s: -s requires a time interval: %s\n", + pmProgname, endnum); + free(endnum); + err++; + } + break; + + case 'U': + username = optarg; + break; + + default: + err++; + break; + } + } + + if (err || optind != argc -1) + usage(); + + pmdaOpenLog(&desc); + logger_init(&desc, argv[optind]); + pmdaConnect(&desc); + loggerMain(&desc); + event_shutdown(); + exit(0); +} diff --git a/src/pmdas/logger/pmns b/src/pmdas/logger/pmns new file mode 100644 index 0000000..4b9827a --- /dev/null +++ b/src/pmdas/logger/pmns @@ -0,0 +1,23 @@ +/* + * Metrics for logger PMDA + * + * Copyright (c) 2011 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +logger { + numclients LOGGER:0:0 + numlogfiles LOGGER:0:1 + param_string LOGGER:0:2 + maxmem LOGGER:0:3 + perfile LOGGER:*:* +} diff --git a/src/pmdas/logger/root b/src/pmdas/logger/root new file mode 100644 index 0000000..51218c4 --- /dev/null +++ b/src/pmdas/logger/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { logger } + +#include "pmns" + diff --git a/src/pmdas/logger/util.c b/src/pmdas/logger/util.c new file mode 100644 index 0000000..c79787f --- /dev/null +++ b/src/pmdas/logger/util.c @@ -0,0 +1,181 @@ +/* + * Utility functions for the logger PMDA. + * + * Copyright (c) 2011 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "util.h" + +void +rstrip(char *str) +{ + char *ptr; + + /* Remove all trailing whitespace. Set ptr to last char of + * string. */ + ptr = str + strlen(str) - 1; + /* While trailing whitespace, move back. */ + while (ptr >= str && isspace((int)*ptr)) { + --ptr; + } + *(ptr+1) = '\0'; /* Now set '\0' as terminal byte. */ +} + +char * +lstrip(char *str) +{ + /* While leading whitespace, move forward. */ + char *ptr = str; + while (*ptr != '\0' && isspace((int)*ptr)) { + ptr++; + } + return ptr; +} + +int +start_cmd(const char *cmd, pid_t *ppid) +{ + pid_t child_pid; + int rc; + int pipe_fds[2]; +#define PARENT_END 0 /* parent end of the pipe */ +#define CHILD_END 1 /* child end of the pipe */ +#define STDOUT_FD 1 /* stdout fd */ + + /* FIXME items: + * (1) Should we be looking to handle shell metachars + * differently? Perhaps we should just allow isalnum()||isspace() + * chars only. + * (2) Do we need to clean up the environment in the child before + * the exec()? Remove things like IFS, CDPATH, ENV, and BASH_ENV. + */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_INFO, "%s: Trying to run command: %s", __FUNCTION__, + cmd); +#endif + + /* Create the pipes. */ +#if defined(HAVE_PIPE2) + rc = pipe2(pipe_fds, O_CLOEXEC|O_NONBLOCK); + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "%s: pipe2() returned %s", __FUNCTION__, + strerror(-rc)); + return rc; + } +#else + rc = pipe(pipe_fds); + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "%s: pipe() returned %s", __FUNCTION__, + strerror(-rc)); + return rc; + } + + /* Set the right flags on the pipes. */ + if (fcntl(pipe_fds[PARENT_END], F_SETFL, O_NDELAY) < 0 + || fcntl(pipe_fds[CHILD_END], F_SETFL, O_NDELAY) < 0) { + __pmNotifyErr(LOG_ERR, "%s: fcntl() returned %s", __FUNCTION__, + strerror(-rc)); + return rc; + } + if (fcntl(pipe_fds[PARENT_END], F_SETFD, O_CLOEXEC) < 0 + || fcntl(pipe_fds[CHILD_END], F_SETFD, O_CLOEXEC) < 0) { + __pmNotifyErr(LOG_ERR, "%s: fcntl() returned %s", __FUNCTION__, + strerror(-rc)); + return rc; + } +#endif + + /* Create the new process. */ + child_pid = fork(); + if (child_pid == 0) { /* child process */ + int i; + + /* Duplicate our pipe fd onto stdout of the child. Note that + * this clears O_CLOEXEC, so the new stdout should stay open + * when we call exec(). */ + if (pipe_fds[CHILD_END] != STDOUT_FD) { + if (dup2(pipe_fds[CHILD_END], STDOUT_FD) < 0) { + __pmNotifyErr(LOG_ERR, "%s: dup2() returned %s", __FUNCTION__, + strerror(errno)); + _exit(127); + } + } + + /* Close all other fds. */ + for (i = 0; i <= pipe_fds[CHILD_END]; i++) { + if (i != STDOUT_FD) { + close(i); + } + } + + /* Actually run the command. */ + execl ("/bin/sh", "sh", "-c", cmd, (char *)NULL); + _exit (127); + + } + else if (child_pid > 0) { /* parent process */ + close (pipe_fds[CHILD_END]); + if (ppid != NULL) { + *ppid = child_pid; + } + } + else if (child_pid < 0) { /* fork error */ + int errno_save = errno; + + __pmNotifyErr(LOG_ERR, "%s: fork() returned %s", __FUNCTION__, + strerror(errno_save)); + close (pipe_fds[PARENT_END]); + close (pipe_fds[CHILD_END]); + + return -errno_save; + } + + return pipe_fds[PARENT_END]; +} + +int +stop_cmd(pid_t pid) +{ + int rc; + pid_t wait_pid; + int wstatus; + + __pmNotifyErr(LOG_INFO, "%s: killing pid %" FMT_PID, __FUNCTION__, pid); + + /* Send the TERM signal. */ + rc = kill(pid, SIGTERM); + __pmNotifyErr(LOG_INFO, "%s: kill returned %d", __FUNCTION__, rc); + + /* Wait for the process to go away. */ + do { + wait_pid = waitpid (pid, &wstatus, 0); + __pmNotifyErr(LOG_INFO, "%s: waitpid returned %d", __FUNCTION__, + (int)wait_pid); + } while (wait_pid == -1 && errno == EINTR); + + /* Return process exit status. */ + return wstatus; +} diff --git a/src/pmdas/logger/util.h b/src/pmdas/logger/util.h new file mode 100644 index 0000000..a26ac2b --- /dev/null +++ b/src/pmdas/logger/util.h @@ -0,0 +1,25 @@ +/* + * Utility routines. + * + * Copyright (c) 2011 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _UTIL_H +#define _UTIL_H + +extern char *lstrip(char *str); +extern void rstrip(char *str); +extern int start_cmd(const char *cmd, pid_t *ppid); +extern int stop_cmd(pid_t pid); + +#endif /* _UTIL_H */ diff --git a/src/pmdas/lustrecomm/GNUmakefile b/src/pmdas/lustrecomm/GNUmakefile new file mode 100644 index 0000000..d7ae6e0 --- /dev/null +++ b/src/pmdas/lustrecomm/GNUmakefile @@ -0,0 +1,63 @@ +# +# Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. +# +# Author: Scott Emery +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = lustrecomm +DOMAIN = LUSTRECOMM +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +DFILES = README +HFILES = libreadfiles.h +CFILES = $(IAM).c file_indexed.c file_single.c refresh_file.c timespec_routines.c + +LIBTARGET = pmda_$(IAM).so +CMDTARGET = pmda$(IAM) +TARGETS = $(CMDTARGET) +#TARGETS = $(LIBTARGET) + +LDOPTS = +LSRCFILES = Install Remove README help pmns root +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_RT) +LCFLAGS = -I. + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(TARGETS) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/$(CMDTARGET) + #$(INSTALL) -m 755 $(LIBTARGET) $(PMDADIR)/$(LIBTARGET) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root pmns domain.h help $(PMDADIR) +else +build-me: +install: +endif + +default_pcp: default + +install_pcp: install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/lustrecomm/Install b/src/pmdas/lustrecomm/Install new file mode 100755 index 0000000..d712c24 --- /dev/null +++ b/src/pmdas/lustrecomm/Install @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. +# +# Author: Scott Emery +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. /etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=lustrecomm +pmda_interface=2 +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/lustrecomm/README b/src/pmdas/lustrecomm/README new file mode 100644 index 0000000..467cd80 --- /dev/null +++ b/src/pmdas/lustrecomm/README @@ -0,0 +1,60 @@ +lustrecomm PMDA +=============== + +This PMDA collects statistics common to all lustre implementations, +the statistics found in /proc/lustre and /proc/lnet + +Note: + This PMDA may be remade from source and hence requires IDO (or + more specifically a C compiler) to be installed. + + Uses of make(1) may fail (without removing or clobbering files) + if the C compiler cannot be found. This is most likely to + happen when running the PMDA ./Install script. + + The only remedial action is to install the C compiler, or + hand-craft changes to the Makefile. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT lustrecomm + +Installation +============ + + + # cd $PCP_PMDAS_DIR/lustrecomm + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/lustrecomm + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/trivial.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/lustrecomm/Remove b/src/pmdas/lustrecomm/Remove new file mode 100755 index 0000000..7ed7151 --- /dev/null +++ b/src/pmdas/lustrecomm/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. +# +# Author: Scott Emery +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# source the PCP configuration environment variables +. /etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=lustrecomm + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/lustrecomm/TODO b/src/pmdas/lustrecomm/TODO new file mode 100644 index 0000000..33b1860 --- /dev/null +++ b/src/pmdas/lustrecomm/TODO @@ -0,0 +1,3 @@ + +v4 - add nis and peers + diff --git a/src/pmdas/lustrecomm/file_indexed.c b/src/pmdas/lustrecomm/file_indexed.c new file mode 100644 index 0000000..2f11c88 --- /dev/null +++ b/src/pmdas/lustrecomm/file_indexed.c @@ -0,0 +1,96 @@ +/* + * Lustre common /proc PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libreadfiles.h" + + +/* int file_indexed (struct file_state *f_s, + * int type, int base, void *vpp, int index) + * - Get single value from file containing multiple values + * struct file_state *f_s - struct for access to single file + * type - PCP type described by the PMAPI PM_TYPE_* enum + * base - for integer types, base as stored in the file + * - for string and aggregate types, size of memory pointed to + * ---------------- size may be changed by realloc + * vpp - pointer to pointer to storage allocated to contain value + * - pointed to pointer may change + * ---------------- pointer may be changed by realloc + * index - value is the n'th strtok token in the file + * consider specifying token seperator, default whitespace is fine for now + */ +int file_indexed (struct file_state *f_s, int type, int *base, void **vpp, int index) +{ + int i; + char *cp, *sp; + + /* reload file if it hasn't been reloaded in x */ + if (refresh_file( f_s ) <0 ) { + __pmNotifyErr(LOG_ERR, "file_indexed: refresh_file error"); + return -1; + } + /* file_indexed assumes that the file contains one line + * if the file may ever contain more than one line, consider file_arrayed + * (if that ever gets written) + * strtok to the appropriate spot + * strtok screws up the target string, so make a copy of the file data + */ + cp = malloc ( f_s->datas ); + strcpy ( cp, f_s->datap ); + sp = strtok( cp, " " ); + for (i = 0; i < index; i++){ + sp = strtok( NULL, " "); + } + /* otherwise, a lot like file_single (see below) + * one would eventually write a configure script to make + * sure that the right routines are used below.. defining + * a STRTO32 and STRTOU32, etc. + */ + switch (type) { + case PM_TYPE_32: + *((long *)(*vpp)) = strtol(sp,0,*base); + break; + case PM_TYPE_U32: + *((unsigned long *)(*vpp)) = strtoul(sp,0,*base); + break; + case PM_TYPE_64: + *((long long *)(*vpp)) = strtoll(sp,0,*base); + break; + case PM_TYPE_U64: + *((unsigned long long *)(*vpp)) = strtoull(sp,0,*base); + break; + case PM_TYPE_FLOAT: + *((float *)(*vpp)) = strtof(sp,0); + break; + case PM_TYPE_DOUBLE: + *((double *)(*vpp)) = strtod(sp,0); + break; + default: + fprintf(stderr,"file_indexed: type %s not supported\n", pmTypeStr(type)); + break; + } + /* so, write it up and slice and dice */ + free (cp); + + return 0; +} + + diff --git a/src/pmdas/lustrecomm/file_single.c b/src/pmdas/lustrecomm/file_single.c new file mode 100644 index 0000000..2f135d2 --- /dev/null +++ b/src/pmdas/lustrecomm/file_single.c @@ -0,0 +1,104 @@ +/* + * Lustre common /proc PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libreadfiles.h" + + +/* file_single (char *filename, int type, int *base, void **vpp) + * - Get a value from a file containing single value + * filename - name of file + * type - PCP type described by the PMAPI PM_TYPE_* enum + * base - for integer types, base as stored in the file + * - for string and aggregate types, size of memory pointed to + * ---------------- size may be changed by realloc + * vpp - pointer to pointer to storage allocated to contain value + * - pointed to pointer may change + * ---------------- pointer may be changed by realloc + * allocating and deallocating space for value is the responsibility + * of the caller + */ +int file_single (char *filename, int type, int *base, void **vpp) +{ + int fd; + int n; + /* overlarge */ + char b[80] = { 0 }; + + /* Read in the file into the buffer b */ + if (( fd = open (filename, O_RDONLY)) < 0) { + perror("file_single: open"); + return -1; + } + /* Okay this presents a problem vis a vis some lustre proc files. + * While the present scheme is adequate for lustre common in most + * cases, some lustre proc files have *ridiculously* large amounts + * of data stored in them. Need to come up with some dynamic + * memory buffer system for other lustre PMDAs + */ + switch (type) { + case PM_TYPE_32: + case PM_TYPE_U32: + case PM_TYPE_64: + case PM_TYPE_U64: + case PM_TYPE_FLOAT: + case PM_TYPE_DOUBLE: + if ((n = read (fd, b, sizeof(b))) < 0 ){ + perror("file_single: read"); + close(fd); + return -1; + } + close(fd); + break; + default: + fprintf(stderr,"file_single: type %s not supported\n", pmTypeStr(type)); + close(fd); + return -1; + + } + /* One would eventually write a configure script to make + * sure that the right routines are used below.. defining + * a STRTO32 and STRTOU32, etc. + */ + switch (type) { + case PM_TYPE_32: + *((long *)(*vpp)) = strtol(b,0,*base); + break; + case PM_TYPE_U32: + *((unsigned long *)(*vpp)) = strtoul(b,0,*base); + break; + case PM_TYPE_64: + *((long long *)(*vpp)) = strtoll(b,0,*base); + break; + case PM_TYPE_U64: + *((unsigned long long *)(*vpp)) = strtoull(b,0,*base); + break; + case PM_TYPE_FLOAT: + *((float *)(*vpp)) = strtof(b,0); + break; + case PM_TYPE_DOUBLE: + *((double *)(*vpp)) = strtod(b,0); + break; + } + return 0; +} + + diff --git a/src/pmdas/lustrecomm/help b/src/pmdas/lustrecomm/help new file mode 100644 index 0000000..2d86915 --- /dev/null +++ b/src/pmdas/lustrecomm/help @@ -0,0 +1,106 @@ +# +# Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. +# +# Author: Scott Emery +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# +# lustrecomm PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ lustrecomm.timeout contents of /proc/sys/lustre/timeout +The time period that a client waits for a server to complete an RPC +(default in 1.6 is 100s). Servers wait half this time for a normal +client RPC to compelte and a quarter of this time for a single +bulk request to complete. The client pings recoverable targets +(MDS and OSTs) at one quarter of the timeout, and the server +waits on and a half times the timeout before evicting a client +for being "stale". (source: Lustre 1.6 Operations Manual) + +@ lustrecomm.ldlm_timeout contents of /proc/sys/lustre/ldlm_timeout +This is the time period for which a server will wait for a client +to reply to an initial AST (lock cancellation request). The default +is 20s for an OST and 6s for an MDS. (source: Lustre 1.6 Operations +Manual) + +@ lustrecomm.dump_on_timeout contents of /proc/sys/lustre/ldlm_timeout +A 1 triggers dumps of the Lustre debug log when timeouts occur. +Default value 0. (source: Lustre 1.6 Operations Manual) + +@ lustrecomm.lustre_memused contents of /proc/sys/lustre/memused +lustre/obdclass/linux/linux-sysctl.c: &proc_memory_alloc +lustre/include/obd_support.h: obd_memory_sum() +Total bytes allocated by Lustre (inferred from lustre/include/obd_support.h) + +@ lustrecomm.lnet_memused contents of /proc/sys/lnet/memused +lnet/libcfs/linux/linux-proc.c: (int *)&libcfs_kmemory.counter +Total bytes allocated by LNET. (inferred from lustre/include/obd_support.h) + +@ lustrecomm.stats.msgs_alloc first number from /proc/sys/lnet/stats +routerstat source file: messages currently allocated (first number after M) + +@ lustrecomm.stats.msgs_max second number from /proc/sys/lnet/stats +routerstat source file: messages maximum (highwater mark) (second +number after M) + +@ lustrecomm.stats.errors third number from /proc/sys/lnet/stats +routerstat source file: errors (number after E) + +@ lustrecomm.stats.send_count fourth number from /proc/sys/lnet/stats +routerstat source file: send_count (raw data from which second number +after S is derived). + +@ lustrecomm.stats.recv_count fifth number from /proc/sys/lnet/stats +routerstat source file: recv_count (raw data from which second number +after R is derived) + +@ lustrecomm.stats.route_count sixth number from /proc/sys/lnet/stats +routerstat source file: route_count (raw data from which second number +after R is derived) + +@ lustrecomm.stats.drop_count seventh number from /proc/sys/lnet/stats +routerstat source file: drop_count (raw data from which second number +after D is derived) + +@ lustrecomm.stats.send_length eigth number from /proc/sys/lnet/stats +routerstat source file: send_length (raw data from which first number +after S is derived) + +@ lustrecomm.stats.recv_length ninth number from /proc/sys/lnet/stats +routerstat source file: recv_length (raw data from which first number +after S is derived) + +@ lustrecomm.stats.route_length tenth number from /proc/sys/lnet/stats +routerstat source file: route_length (raw data from which first number +after R is derived) + +@ lustrecomm.stats.drop_length eleventh number from /proc/sys/lnet/stats +routerstat source file: drop_length (raw data from which first number +after D is derived) + diff --git a/src/pmdas/lustrecomm/libreadfiles.h b/src/pmdas/lustrecomm/libreadfiles.h new file mode 100644 index 0000000..121dcdf --- /dev/null +++ b/src/pmdas/lustrecomm/libreadfiles.h @@ -0,0 +1,59 @@ +/* + * Lustre common /proc PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include +#include +#include +#include +#include +#include + +struct file_state { + struct timespec ts; + char *filename; + int fd; + int datas; + void *datap; +}; + +#define BUFFERBLOCK 4096 + +#ifndef FILE_TIME_OFFSET +extern struct timespec file_time_offset; +#endif + +/* timespec_routines.c */ +extern struct timespec timespec_add (struct timespec *a, struct timespec *b); +extern int timespec_le ( struct timespec *lhs, struct timespec *rhs); +/* refresh_file.c */ +extern int refresh_file( struct file_state *f_s ); +/* file_indexed.c */ +extern int file_indexed (struct file_state *f_s, int type, int *base, void **vpp, int index); +/* file_single.c */ +extern int file_single (char *filename, int type, int *base, void **vpp); + + diff --git a/src/pmdas/lustrecomm/lustrecomm.c b/src/pmdas/lustrecomm/lustrecomm.c new file mode 100644 index 0000000..9b4b9df --- /dev/null +++ b/src/pmdas/lustrecomm/lustrecomm.c @@ -0,0 +1,304 @@ +/* + * Lustre common /proc PMDA + * + * Original author: Scott Emery + * + * Copyright (c) 2012,2014 Red Hat. + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "libreadfiles.h" +#include +#include "domain.h" + + +/* + * Lustrecomm PMDA + * + * This PMDA gathers Lustre statistics from /proc, it is constructed using + * libpcp_pmda. + * + * + * Metrics + * lustrecomm.time - time in seconds since the 1st of Jan, 1970. + */ + +#define PROC_SYS_LNET_STATS 0 +#define PROC_SYS_LNET_NIS 1 +#define PROC_SYS_LNET_PEERS 2 +#define FILESTATETABSIZE 3 + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +static pmdaMetric metrictab[] = { +/* timeout */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, +/* ldlm_timeout */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, +/* dump_on_timeout */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, +/* lustre_memused */ + { NULL, + { PMDA_PMID(0,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* lnet_memused */ + { NULL, + { PMDA_PMID(0,4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* data pulled from /proc/sys/lnet/stats */ +/*0 42 0 22407486 23426580 0 0 135850271989 472430974209 0 0*/ +/* stats.msgs_alloc */ + { NULL, + { PMDA_PMID(1,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.msgs_max */ + { NULL, + { PMDA_PMID(1,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.errors */ + { NULL, + { PMDA_PMID(1,2), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.send_count */ + { NULL, + { PMDA_PMID(1,3), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.recv_count */ + { NULL, + { PMDA_PMID(1,4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.route_count */ + { NULL, + { PMDA_PMID(1,5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.drop_count */ + { NULL, + { PMDA_PMID(1,6), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.send_length */ + { NULL, + { PMDA_PMID(1,7), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.recv_length */ + { NULL, + { PMDA_PMID(1,8), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.route_length */ + { NULL, + { PMDA_PMID(1,9), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* stats.drop_length */ + { NULL, + { PMDA_PMID(1,10), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } } + +}; + +struct file_state filestatetab[] = { + { { 0 , 0 } , "/proc/sys/lnet/stats", 0, 0, NULL}, + { { 0 , 0 } , "/proc/sys/lnet/nis", 0, 0, NULL}, + { { 0 , 0 } , "/proc/sys/lnet/peers", 0, 0, NULL} +}; + +static int isDSO = 1; /* =0 I am a daemon */ +static char mypath[MAXPATHLEN]; +static char *username; + +/* + * callback provided to pmdaFetch + */ + +static int +lustrecomm_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + void *vp; + + /* check for PMID errors */ + if ( !((idp->cluster == 0) && (idp->item <= 4)) && + !((idp->cluster == 1) && (idp->item <= 10)) ) + return PM_ERR_PMID; + else if (inst != PM_IN_NULL) + return PM_ERR_INST; + + /* refresh and store data */ + if (idp->cluster == 0) { + vp = malloc (4); + int aux = 10; + /* not saving any time fussing with "how recent", just do it */ + switch (idp->item) { + case 0: + /* consider mdesc->m_desc.type */ + /* problem: the way it's stored in PCP isn't necessarily */ + /* the way it's presented by the OS */ + file_single("/proc/sys/lustre/timeout", PM_TYPE_32, &aux, &vp); + atom->l = *((long *) vp); + break; + case 1: + file_single("/proc/sys/lustre/ldlm_timeout", PM_TYPE_32, &aux, &vp); + atom->l = *((long *) vp); + break; + case 2: + file_single("/proc/sys/lustre/dump_on_timeout", PM_TYPE_32, &aux, &vp); + atom->l = *((long *) vp); + break; + case 3: + file_single("/proc/sys/lustre/memused", PM_TYPE_32, &aux, &vp); + atom->l = *((long *) vp); + break; + case 4: + file_single("/proc/sys/lnet/lnet_memused", PM_TYPE_32, &aux, &vp); + atom->l = *((long *) vp); + break; + default: + printf ("PMID %d:%d non-existant\n",idp->cluster,idp->item); + break; + } + free (vp); + } + if (idp->cluster == 1) { + vp = malloc (4); + int aux = 10; + + switch(idp->item) { + case 0: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 0); + atom->l = *((long *) vp); + break; + case 1: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 1); + atom->l = *((long *) vp); + break; + case 2: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 2); + atom->l = *((long *) vp); + break; + case 3: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 3); + atom->l = *((long *) vp); + break; + case 4: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 4); + atom->l = *((long *) vp); + break; + case 5: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 5); + atom->l = *((long *) vp); + break; + case 6: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_32,&aux, &vp, 6); + atom->l = *((long *) vp); + break; + case 7: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_64,&aux, &vp, 7); + atom->l = *((long *) vp); + break; + case 8: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_64,&aux, &vp, 8); + atom->l = *((long *) vp); + break; + case 9: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_64,&aux, &vp, 9); + atom->l = *((long *) vp); + break; + case 10: + file_indexed(&filestatetab[PROC_SYS_LNET_STATS], PM_TYPE_64,&aux, &vp, 10); + atom->l = *((long *) vp); + break; + default: + break; + } + free(vp); + } + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +lustrecomm_init(pmdaInterface *dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "lustrecomm" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_2, "lustrecomm DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + pmdaSetFetchCallBack(dp, lustrecomm_fetchCallBack); + + pmdaInit(dp, NULL, 0, + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface desc; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "lustrecomm" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, LUSTRECOMM, + "lustrecomm.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&desc); + lustrecomm_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + exit(0); +} diff --git a/src/pmdas/lustrecomm/pmns b/src/pmdas/lustrecomm/pmns new file mode 100644 index 0000000..49a999f --- /dev/null +++ b/src/pmdas/lustrecomm/pmns @@ -0,0 +1,47 @@ +/* + * Metrics for lustrecomm PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +lustrecomm { + timeout LUSTRECOMM:0:0 + ldlm_timeout LUSTRECOMM:0:1 + dump_on_timeout LUSTRECOMM:0:2 + lustre_memused LUSTRECOMM:0:3 + lnet_memused LUSTRECOMM:0:4 + stats +} + +lustrecomm.stats { +/* data pulled from /proc/sys/lnet/stats */ +/*0 42 0 22407486 23426580 0 0 135850271989 472430974209 0 0*/ + msgs_alloc LUSTRECOMM:1:0 + msgs_max LUSTRECOMM:1:1 + errors LUSTRECOMM:1:2 + send_count LUSTRECOMM:1:3 + recv_count LUSTRECOMM:1:4 + route_count LUSTRECOMM:1:5 + drop_count LUSTRECOMM:1:6 + send_length LUSTRECOMM:1:7 + recv_length LUSTRECOMM:1:8 + route_length LUSTRECOMM:1:9 + drop_length LUSTRECOMM:1:10 +} + diff --git a/src/pmdas/lustrecomm/pmns.v4 b/src/pmdas/lustrecomm/pmns.v4 new file mode 100644 index 0000000..e702751 --- /dev/null +++ b/src/pmdas/lustrecomm/pmns.v4 @@ -0,0 +1,87 @@ +/* + * Metrics for lustrecomm PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +lustrecomm { + timeout LUSTRECOMM:0:0 + ldlm_timeout LUSTRECOMM:0:1 + dump_on_timeout LUSTRECOMM:0:2 + lustre_memused LUSTRECOMM:0:3 + lnet_memused LUSTRECOMM:0:4 + stats +} + +lustrecomm.stats { +/* data pulled from /proc/sys/lnet/stats */ +/*0 42 0 22407486 23426580 0 0 135850271989 472430974209 0 0*/ + msgs_alloc LUSTRECOMM:1:0 + msgs_max LUSTRECOMM:1:1 + errors LUSTRECOMM:1:2 + send_count LUSTRECOMM:1:3 + recv_count LUSTRECOMM:1:4 + route_count LUSTRECOMM:1:5 + drop_count LUSTRECOMM:1:6 + send_length LUSTRECOMM:1:7 + recv_length LUSTRECOMM:1:8 + route_length LUSTRECOMM:1:9 + drop_length LUSTRECOMM:1:10 +} + +lustrecomm.nis { +/* data pulled from /proc/sys/lnet/nis */ +/* nid refs peer max tx min */ +/* 0@lo 2 0 0 0 0 */ +/* 10.149.0.1@o2ib 13 8 64 64 26 */ + + nid LUSTRECOMM:2:1 + refs LUSTRECOMM:2:2 + peer LUSTRECOMM:2:3 + max LUSTRECOMM:2:4 + tx LUSTRECOMM:2:5 + tx_min LUSTRECOMM:2:6 + active LUSTRECOMM:2:7 +/* subtracting max - tx yields the nubmer of sends currently active*/ +/* a large or increasing number of active sends may be a problem */ +} + +lustrecomm.peers { +/* data pulled from /proc/sys/lnet/peers */ +/* nid refs state max rtr min tx min queue */ +/* 10.149.2.15@o2ib 1 ~rtr 8 8 8 8 0 0 */ +/* 10.149.2.29@o2ib 1 ~rtr 8 8 8 8 -26 0 */ + nid LUSTRECOMM:3:0 + refs LUSTRECOMM:3:1 + state LUSTRECOMM:3:2 + max LUSTRECOMM:3:3 + rtr LUSTRECOMM:3:4 + rtr_min LUSTRECOMM:3:5 + tx LUSTRECOMM:3:6 + tx_min LUSTRECOMM:3:7 + queue LUSTRECOMM:3:8 + in_progress LUSTRECOMM:3:9 +/* if rtr/tx is less than max, there are operations in progress. The number + of operations is equal to rtr or tx subtraced from max. +*/ + blocking LUSTRECOMM:2:10 +/* if rtr/tx is greater than max, there are operations blocking */ +} + + diff --git a/src/pmdas/lustrecomm/refresh_file.c b/src/pmdas/lustrecomm/refresh_file.c new file mode 100644 index 0000000..86ca16f --- /dev/null +++ b/src/pmdas/lustrecomm/refresh_file.c @@ -0,0 +1,85 @@ +/* + * Lustre common /proc PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#define FILE_TIME_OFFSET 1 + +#include "libreadfiles.h" + +static struct timespec file_time_offset = { 0 , 900000000 }; + + +int refresh_file( struct file_state *f_s ){ + struct timespec now, tmp; + int i = 0; + + if (f_s->datap) { + /* get time */ + if ( clock_gettime(CLOCK_MONOTONIC, &now) < 0 ) { + /* if we don't know what time it is */ + /* there's nothing we can do with this */ + return 0; + } + /* if time since last refresh > delta */ + tmp = timespec_add( &f_s->ts, &file_time_offset); + if ( timespec_le( &now, &tmp) ) { + /* file is recent */ + return 0; + } + f_s->ts = now; + /* clear old data, make errors obvious, autoterm trailing strings */ + memset ( f_s->datap, 0, f_s->datas ); + } + /* if fd is null open file */ + if ( f_s->fd <= 0) { + if (( f_s->fd = open (f_s->filename, O_RDONLY)) < 0) { + perror("refresh_file: open"); + return -1; + } + } + if ( lseek (f_s->fd, 0, SEEK_SET) < 0 ) { + perror("refresh_file: initial seek"); + return -1; + } + /* only grow, never shrink... what would be the point? */ + while (f_s->datap && (i = read (f_s->fd, f_s->datap, f_s->datas)) >= f_s->datas ){ + /* oh heck, what do I do if this fails? */ + f_s->datas += BUFFERBLOCK; + if ((f_s->datap = realloc(f_s->datap, f_s->datas)) == NULL){ + free((char *)f_s->datap); + perror("refresh_file: realloc"); + return -1; + } + if ( lseek (f_s->fd, 0, SEEK_SET) < 0 ) { + perror("refresh_file: subsequent seek"); + return -1; + } + } + if (i < 0 ){ + /* read failed */ + perror("refresh_file: file read failed"); + return -1; + } + return 0; +} + + diff --git a/src/pmdas/lustrecomm/root b/src/pmdas/lustrecomm/root new file mode 100644 index 0000000..bf7781c --- /dev/null +++ b/src/pmdas/lustrecomm/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { lustrecomm } + +#include "pmns" + diff --git a/src/pmdas/lustrecomm/timespec_routines.c b/src/pmdas/lustrecomm/timespec_routines.c new file mode 100644 index 0000000..976be2a --- /dev/null +++ b/src/pmdas/lustrecomm/timespec_routines.c @@ -0,0 +1,48 @@ +/* + * Lustre common /proc PMDA + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + * + * Author: Scott Emery + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + + +struct timespec timespec_add (struct timespec *a, struct timespec *b){ + struct timespec tret; + + tret.tv_nsec = (a->tv_nsec + b->tv_nsec) % 1000000000; + tret.tv_sec = a->tv_sec + b->tv_sec + + ((a->tv_nsec + b->tv_nsec)/1000000000); + return tret; +} + +int timespec_le ( struct timespec *lhs, struct timespec *rhs) { + if (rhs->tv_sec < lhs->tv_sec) { + /* false */ + return 0; + } + if (lhs->tv_sec == rhs->tv_sec) { + if (rhs->tv_nsec < lhs->tv_nsec) { + /* false */ + return 0; + } + } + return 1; +} + diff --git a/src/pmdas/mailq/GNUmakefile b/src/pmdas/mailq/GNUmakefile new file mode 100644 index 0000000..1794f1a --- /dev/null +++ b/src/pmdas/mailq/GNUmakefile @@ -0,0 +1,49 @@ +# +# Copyright (c) 2000-2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mailq +DOMAIN = MAILQ + +CMDTARGET = pmdamailq$(EXECSUFFIX) +CFILES = mailq.c +DFILES = README +LSRCFILES = Install Remove root help pmns $(DFILES) pmlogconf.summary +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_REGEX) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LDIRT = domain.h *.log *.dir *.pag so_locations + +default: $(CMDTARGET) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/$(CMDTARGET) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns domain.h $(PMDADIR) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary + +mailq.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/mailq/Install b/src/pmdas/mailq/Install new file mode 100644 index 0000000..efe1fd5 --- /dev/null +++ b/src/pmdas/mailq/Install @@ -0,0 +1,117 @@ +#! /bin/sh +# +# Copyright (c) 1997-2000,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the mailq PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mailq +pmda_interface=2 +forced_restart=false + +# Do it +# +pmdaSetup + +if $do_pmda +then + + dso_opt=false + socket_opt=false + pipe_opt=true + + mqueue="" + chk="" + + if [ -f /etc/sendmail.cf ] + then + chk=`sed -n '/^O *QueueDirectory *= */s///p' /etc/sendmail.cf` + [ -z "$chk" ] && chk=`sed -n '/^O *Q *\//s//\//p' /etc/sendmail.cf` + fi + + if [ ! -z "$chk" -a -d "$chk" ] + then + mqueue="$chk" + else + mqueue=/var/spool/mqueue + fi + + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N 'Mail queue directory ['"$mqueue"'] '"$PCP_ECHO_C" + read ans + [ -z "$ans" ] && break + if [ -d "$ans" ] + then + mqueue="$ans" + break + fi + echo "Error: \"$ans\" is not a directory" + done + + echo + regex="" + $PCP_ECHO_PROG $PCP_ECHO_N 'Mail basename regex ['"$regex"'] '"$PCP_ECHO_C" + read regex + [ -z "$regex" ] || args="$args -r $regex" + + echo + args="$args $mqueue" + + while true + do + echo 'The default delay thresholds for grouping the pending mail items are:' + echo ' 1 hour, 4 hours, 8 hours, 1 day, 3 days and 7 days' + echo + $PCP_ECHO_PROG $PCP_ECHO_N 'Do you wish to use the default delay thresholds [y]? '"$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + break + else + bucketlist='' + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N 'Threshold? [return if no more] '"$PCP_ECHO_C" + read ans + [ -z "$ans" ] && break + # strip blanks so args in pmcd.conf get passed correctly to + # the mailqpmda binary + # + ans=`echo "$ans" | sed -e 's/ //g'` + if [ -z "$bucketlist" ] + then + bucketlist="$ans" + else + bucketlist="$bucketlist,$ans" + fi + done + if [ ! -z "$bucketlist" ] + then + args="$args -b $bucketlist" + break + fi + echo + echo 'Error: you must specify at least one threshold' + echo + fi + done + echo +fi + +pmdaInstall + +exit 0 diff --git a/src/pmdas/mailq/README b/src/pmdas/mailq/README new file mode 100644 index 0000000..08bbfb6 --- /dev/null +++ b/src/pmdas/mailq/README @@ -0,0 +1,48 @@ +Mailq PMDA +========== + +This PMDA exports information about the sendmail(1) queue. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT mailq + +Installation +============ + + + # cd $PCP_PMDAS_DIR/mailq + + + Check that there is no clash in the Performance Metrics Domain defined + in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/mailq + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/mailq.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/mailq/Remove b/src/pmdas/mailq/Remove new file mode 100644 index 0000000..77da216 --- /dev/null +++ b/src/pmdas/mailq/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the mailq PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=mailq + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/mailq/help b/src/pmdas/mailq/help new file mode 100644 index 0000000..29a1792 --- /dev/null +++ b/src/pmdas/mailq/help @@ -0,0 +1,52 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# mailq PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ MAILQ.1 Instance domain used to "bin" messages based on how long they have been deferred + + +@ mailq.length Number of messages in the queue +The total number of messages in the mail queue. + +@ mailq.deferred Histogram of pending messages based on how long they have been deferred +Counts of the number of messages in the sendmail queue grouped by +how long the mail delivery has been deferred. + +The groups are based on the how long a message has been in the +queue: + 1-week at least a week + 3-days at least 3 days and less than a week + 1-day at least one day and less than 3 days + 8-hours more than 8 hours and less than one day + 4-hours between 4 and 8 hours + 1-hour between 1 and 4 hours + recent less than an hour diff --git a/src/pmdas/mailq/mailq.c b/src/pmdas/mailq/mailq.c new file mode 100644 index 0000000..8d82fcd --- /dev/null +++ b/src/pmdas/mailq/mailq.c @@ -0,0 +1,401 @@ +/* + * Mailq PMDA + * + * Copyright (c) 2012,2014 Red Hat. + * Copyright (c) 1997-2000,2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#ifdef HAVE_REGEX_H +#include +#endif +#include + +/* + * histogram for binning messages based on queue time + */ +typedef struct { + long count; /* number in this bin */ + time_t delay; /* in queue for at least this long (seconds) */ +} histo_t; + +static histo_t *histo; +static int numhisto; +static int queue; + +/* + * list of instances - indexes must match histo[] above + */ +static pmdaInstid *_delay; + +static char *queuedir = "/var/spool/mqueue"; +static char startdir[MAXPATHLEN]; +static char *username; + +static char *regexstring; +static regex_t mq_regex; + +/* + * list of instance domains + */ +static pmdaIndom indomtab[] = { +#define DELAY_INDOM 0 + { DELAY_INDOM, 0, NULL }, +}; + +/* + * all metrics supported in this PMDA - one table entry for each + */ +static pmdaMetric metrictab[] = { +/* length */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* deferred */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_U32, DELAY_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +}; + +static int +mailq_histogram(char *option) +{ + struct timeval tv; + char *errmsg; + char *q; + int sts; + + q = strtok(option, ","); + while (q != NULL) { + if ((sts = pmParseInterval((const char *)q, &tv, &errmsg)) < 0) { + pmprintf("%s: bad historgram bins argument:\n%s\n", pmProgname, errmsg); + free(errmsg); + return -EINVAL; + } + numhisto++; + histo = (histo_t *)realloc(histo, numhisto * sizeof(histo[0])); + if (histo == NULL) + __pmNoMem("histo", numhisto * sizeof(histo[0]), PM_FATAL_ERR); + histo[numhisto-1].delay = tv.tv_sec; + q = strtok(NULL, ","); + } + return 0; +} + +static int +compare_delay(const void *a, const void *b) +{ + histo_t *ha = (histo_t *)a; + histo_t *hb = (histo_t *)b; + + return hb->delay - ha->delay; +} + +/* + * callback provided to pmdaFetch + */ +static int +mailq_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int b; + + if (idp->cluster == 0) { + if (idp->item == 0) { /* mailq.length */ + if (inst == PM_IN_NULL) + atom->ul = queue; + else + return PM_ERR_INST; + } + else if (idp->item == 1) { /* mailq.deferred */ + /* inst is unsigned, so always >= 0 */ + for (b = 0; b < numhisto; b++) { + if (histo[b].delay == inst) break; + } + if (b < numhisto) + atom->ul = histo[b].count; + else + return PM_ERR_INST; + } + else + return PM_ERR_PMID; + } + else + return PM_ERR_PMID; + + return 0; +} + +/* + * wrapper for pmdaFetch which refreshes the metrics + */ +static int +mailq_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + static int warn = 0; + int num; + int i; + int b; + struct stat sbuf; + time_t now; + static time_t last_refresh = 0; + time_t waiting; + char *p; + struct dirent **list; + + time(&now); + + /* clip refresh rate to at most once per 30 seconds */ + if (now - last_refresh > 30) { + last_refresh = now; + + queue = 0; + for (b = 0; b < numhisto; b++) + histo[b].count = 0; + + if (chdir(queuedir) < 0) { + if (warn == 0) { + __pmNotifyErr(LOG_ERR, "chdir(\"%s\") failed: %s\n", + queuedir, osstrerror()); + warn = 1; + } + } + else { + if (warn == 1) { + __pmNotifyErr(LOG_INFO, "chdir(\"%s\") success\n", queuedir); + warn = 0; + } + + num = scandir(".", &list, NULL, NULL); + + for (i = 0; i < num; i++) { + p = list[i]->d_name; + /* only file names that match the regular expression */ + if (regexstring && regexec(&mq_regex, list[i]->d_name, 0, NULL, 0)) + continue; + else if (!regexstring && (*p != 'd' || *(p+1) != 'f')) + continue; + if (stat(p, &sbuf) != 0) { + /* + * ENOENT expected sometimes if sendmail is doing its job + */ + if (oserror() == ENOENT) + continue; + fprintf(stderr, "stat(\"%s\"): %s\n", p, osstrerror()); + continue; + } + if (sbuf.st_size > 0 && S_ISREG(sbuf.st_mode)) { + /* really in the queue */ +#if defined(HAVE_ST_MTIME_WITH_E) + waiting = now - sbuf.st_mtime; +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + waiting = now - sbuf.st_mtimespec.tv_sec; +#else + waiting = now - sbuf.st_mtim.tv_sec; +#endif + for (b = 0; b < numhisto; b++) { + if (waiting >= histo[b].delay) { + histo[b].count++; + break; + } + } + queue++; + } + } + for (i = 0; i < num; i++) + free(list[i]); + if (num > 0) + free(list); + } + if (chdir(startdir) < 0) { + __pmNotifyErr(LOG_ERR, "chdir(\"%s\") failed: %s\n", + startdir, osstrerror()); + } + } + + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * Initialise the agent (daemon only). + */ +void +mailq_init(pmdaInterface *dp) +{ + if (dp->status != 0) + return; + + __pmSetProcessIdentity(username); + dp->version.two.fetch = mailq_fetch; + pmdaSetFetchCallBack(dp, mailq_fetchCallBack); + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); +} + +pmdaInterface dispatch; + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + { "binlist", 1, 'b', "TIMES", "comma-separated histogram bins times" }, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + { "regex", 1, 'r', "RE", "regular expression for matching mail file names" }, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "b:D:d:l:r:U:?", + .long_options = longopts, + .short_usage = "[options] [queuedir]", +}; + +/* + * Set up the agent, running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + int c; + int i; + char namebuf[30]; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + if (getcwd(startdir, sizeof(startdir)) == NULL) { + fprintf(stderr, "%s: getcwd() failed: %s\n", + pmProgname, pmErrStr(-oserror())); + exit(1); + } + + snprintf(mypath, sizeof(mypath), "%s%c" "mailq" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, MAILQ, + "mailq.log", mypath); + + while ((c = pmdaGetOptions(argc, argv, &opts, &dispatch)) != EOF) { + switch (c) { + case 'b': + if (mailq_histogram(opts.optarg) < 0) + opts.errors++; + break; + + case 'r': + regexstring = opts.optarg; + c = regcomp(&mq_regex, regexstring, REG_EXTENDED | REG_NOSUB); + if (c != 0) { + regerror(c, &mq_regex, mypath, sizeof(mypath)); + pmprintf("%s: cannot compile regular expression: %s\n", + pmProgname, mypath); + opts.errors++; + } + break; + } + } + + if (opts.optind == argc - 1) + queuedir = argv[opts.optind]; + else if (opts.optind != argc) + opts.errors++; + + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + if (opts.username) + username = opts.username; + + if (histo == NULL) { + /* default histo bins, if not already done above ... */ + numhisto = 7; + histo = (histo_t *)malloc(numhisto * sizeof(histo[0])); + if (histo == NULL) { + __pmNoMem("histo", numhisto * sizeof(histo[0]), PM_FATAL_ERR); + } + histo[0].delay = 7 * 24 * 3600; + histo[1].delay = 3 * 24 * 3600; + histo[2].delay = 24 * 3600; + histo[3].delay = 8 * 3600; + histo[4].delay = 4 * 3600; + histo[5].delay = 1 * 3600; + histo[6].delay = 0; + } + else { + /* need to add last one and sort on descending time */ + numhisto++; + histo = (histo_t *)realloc(histo, numhisto * sizeof(histo[0])); + if (histo == NULL) { + __pmNoMem("histo", numhisto * sizeof(histo[0]), PM_FATAL_ERR); + } + histo[numhisto-1].delay = 0; + qsort(histo, numhisto, sizeof(histo[0]), compare_delay); + } + + _delay = (pmdaInstid *)malloc(numhisto * sizeof(_delay[0])); + if (_delay == NULL) + __pmNoMem("_delay", numhisto * sizeof(_delay[0]), PM_FATAL_ERR); + + for (i = 0; i < numhisto; i++) { + time_t tmp; + _delay[i].i_inst = histo[i].delay; + histo[i].count = 0; + if (histo[i].delay == 0) + sprintf(namebuf, "recent"); + else if (histo[i].delay < 60) + sprintf(namebuf, "%d-secs", (int)histo[i].delay); + else if (histo[i].delay < 60 * 60) { + tmp = histo[i].delay / 60; + if (tmp <= 1) + sprintf(namebuf, "1-min"); + else + sprintf(namebuf, "%d-mins", (int)tmp); + } + else if (histo[i].delay < 24 * 60 * 60) { + tmp = histo[i].delay / (60 * 60); + if (tmp <= 1) + sprintf(namebuf, "1-hour"); + else + sprintf(namebuf, "%d-hours", (int)tmp); + } + else { + tmp = histo[i].delay / (24 * 60 * 60); + if (tmp <= 1) + sprintf(namebuf, "1-day"); + else + sprintf(namebuf, "%d-days", (int)tmp); + } + _delay[i].i_name = strdup(namebuf); + if (_delay[i].i_name == NULL) { + __pmNoMem("_delay[i].i_name", strlen(namebuf), PM_FATAL_ERR); + } + } + + indomtab[DELAY_INDOM].it_numinst = numhisto; + indomtab[DELAY_INDOM].it_set = _delay; + + pmdaOpenLog(&dispatch); + mailq_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/mailq/pmlogconf.summary b/src/pmdas/mailq/pmlogconf.summary new file mode 100644 index 0000000..a0534a0 --- /dev/null +++ b/src/pmdas/mailq/pmlogconf.summary @@ -0,0 +1,5 @@ +#pmlogconf-setup 2.0 +ident mailq PMDA summary information +probe mailq.length exists ? include : exclude + mailq.length + mailq.deferred diff --git a/src/pmdas/mailq/pmns b/src/pmdas/mailq/pmns new file mode 100644 index 0000000..3de37aa --- /dev/null +++ b/src/pmdas/mailq/pmns @@ -0,0 +1,24 @@ +/* + * Metrics for mailq PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +mailq { + length MAILQ:0:0 + deferred MAILQ:0:1 +} diff --git a/src/pmdas/mailq/root b/src/pmdas/mailq/root new file mode 100644 index 0000000..eab4db5 --- /dev/null +++ b/src/pmdas/mailq/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { mailq } + +#include "pmns" + diff --git a/src/pmdas/memcache/GNUmakefile b/src/pmdas/memcache/GNUmakefile new file mode 100644 index 0000000..7edf21c --- /dev/null +++ b/src/pmdas/memcache/GNUmakefile @@ -0,0 +1,54 @@ +#!gmake +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = memcache +DOMAIN = MEMCACHE +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl client.pl + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/memcache/Install b/src/pmdas/memcache/Install new file mode 100755 index 0000000..a538702 --- /dev/null +++ b/src/pmdas/memcache/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the memcached PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=memcache +perl_opt=true +daemon_opt=false +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/memcache/Remove b/src/pmdas/memcache/Remove new file mode 100755 index 0000000..4c39094 --- /dev/null +++ b/src/pmdas/memcache/Remove @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the memcached PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=memcache +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/memcache/client.pl b/src/pmdas/memcache/client.pl new file mode 100755 index 0000000..bcd3480 --- /dev/null +++ b/src/pmdas/memcache/client.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cache::Memcached; +use vars qw( $memd $val ); + +$memd = new Cache::Memcached { + 'servers' => [ "127.0.0.1:11211" ], + 'debug' => 0, + 'compress_threshold' => 10_000, +}; + +$memd->set("my_key", "Some value"); +$memd->set("object_key", { 'complex' => [ "object", 2, 4 ]}); + +$val = $memd->get("my_key"); +$val = $memd->get("object_key"); +if ($val) { print $val->{'complex'}->[2]; } + +$memd->incr("key"); +$memd->decr("key"); +$memd->incr("key", 2); + diff --git a/src/pmdas/memcache/pmdamemcache.pl b/src/pmdas/memcache/pmdamemcache.pl new file mode 100644 index 0000000..de232b2 --- /dev/null +++ b/src/pmdas/memcache/pmdamemcache.pl @@ -0,0 +1,307 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# The memcached PMDA +# NOTE: Not all metrics are exported at the moment, in particular, +# the per-slab and per-item statistics are not. It may be better to +# manage instances differently as these values are dynamic - perhaps +# have the monitored memcaches (the current indom) in the namespace, +# like the old DBMS PMDAs. +# +use strict; +use warnings; +use PCP::PMDA; +use vars qw( $pmda $id $n %caches ); + +my $memcache_delay = 5; # refresh rate in seconds +my @memcache_instances = ( 0 => '127.0.0.1:11211', + # 1 => '127.0.0.1:11212', + # 2 => '192.168.5.76:11211', + ); +# Configuration files for overriding the above settings +for my $file ( pmda_config('PCP_PMDAS_DIR') . '/memcache/memcache.conf', + './memcache.conf' ) { + eval `cat $file` unless ! -f $file; +} + +my $memcache_indom = 0; # one for each memcached +my $query = "stats\r\nstats slabs\r\nstats items\r\n"; # sent to memcached + +sub memcache_stats_callback +{ + ( $id, $_ ) = @_; + # $pmda->log("memcache_stats_callback: id $id"); + + if (/^STAT items:(\d+):(\w+) (\d+)/) { # stats items + # $caches{$id}{"item$1"}{$2} = $3; + } + elsif (/^STAT (\d+):(\w+) (\d+)/) { # stats slabs + # $caches{$id}{"slab$1"}{$2} = $3; + } + elsif (/^STAT (\w+) (\d+)/) { # stats + $caches{$id}{$1} = $2; + } + elsif (!(/^END/)) { # unknown + $pmda->log("Eh?: $_"); + } +} + +sub memcache_connect +{ + # $pmda->log("memcache_connect: $#memcache_instances"); + + for ($id = 0; $id < $#memcache_instances; $id += 2) { + my ($host, $port) = split(/:/, $memcache_instances[$id+1]); + $pmda->add_sock($host, $port, \&memcache_stats_callback, $id); + } +} + +sub memcache_timer_callback +{ + # $pmda->log("memcache_timer_callback"); + + for ($id = 0; $id < $#memcache_instances; $id += 2) { + $pmda->put_sock($id, $query); + } +} + +sub memcache_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + + # $pmda->log("memcache_fetch_callback: $cluster:$item ($inst)"); + + return (PM_ERR_INST, 0) unless ($inst != PM_IN_NULL); + return (PM_ERR_INST, 0) unless ($inst < $#memcache_instances); + $id = $memcache_instances[$inst]; + return (PM_ERR_AGAIN, 0) unless defined($caches{$id}); + + if ($cluster == 0) { + if ($item == 0) { return ($caches{$id}{'pid'}, 1); } + elsif ($item == 1) { return ($caches{$id}{'uptime'}, 1); } + elsif ($item == 2) { return ($caches{$id}{'curr_items'}, 1); } + elsif ($item == 3) { return ($caches{$id}{'total_items'}, 1); } + elsif ($item == 4) { return ($caches{$id}{'bytes'}, 1); } + elsif ($item == 5) { return ($caches{$id}{'curr_connections'}, 1); } + elsif ($item == 6) { return ($caches{$id}{'total_connections'}, 1); } + elsif ($item == 7) { return ($caches{$id}{'connection_structures'}, 1); } + elsif ($item == 8) { return ($caches{$id}{'cmd_get'}, 1); } + elsif ($item == 9) { return ($caches{$id}{'cmd_set'}, 1); } + elsif ($item == 10) { return ($caches{$id}{'get_hits'}, 1); } + elsif ($item == 11) { return ($caches{$id}{'get_misses'}, 1); } + elsif ($item == 12) { return ($caches{$id}{'bytes_read'}, 1); } + elsif ($item == 13) { return ($caches{$id}{'bytes_written'}, 1); } + elsif ($item == 14) { return ($caches{$id}{'limit_maxbytes'}, 1); } + } +# elsif ($cluster == 1) { +# # many different slabs (X..Y), and 7 metrics in this cluster +# if ($item > 7 * 11) { return (PM_ERR_PMID, 0); } +# $id = int($item / 7) + 6; +# $item %= 7; +# my $slab = "slab$id"; +# +# return (PM_ERR_AGAIN, 0) unless defined($caches{$id}{$slab}); +# if ($item == 0) { return ($caches{$id}{$slab}{'chunk_size'}, 1); } +# elsif ($item == 1) { return ($caches{$id}{$slab}{'chunks_per_page'}, 1); } +# elsif ($item == 2) { return ($caches{$id}{$slab}{'total_pages'}, 1); } +# elsif ($item == 3) { return ($caches{$id}{$slab}{'total_chunks'}, 1); } +# elsif ($item == 4) { return ($caches{$id}{$slab}{'used_chunks'}, 1); } +# elsif ($item == 5) { return ($caches{$id}{$slab}{'free_chunks'}, 1); } +# elsif ($item == 6) { return ($caches{$id}{$slab}{'free_chunks_end'}, 1); } +# } + elsif ($cluster == 2) { + if ($item == 0) { return ($caches{$id}{'active_slabs'}, 1); } + elsif ($item == 1) { return ($caches{$id}{'total_malloced'}, 1); } + } +# elsif ($cluster == 3) { +# # many different slabs (X..Y), and 2 metrics in this cluster +# if ($item > 2 * [Y]) { return (PM_ERR_PMID, 0); } +# $id = int($item / 2) + [X]; +# $item %= 2; +# my $itemid = "item$id"; +# +# return (PM_ERR_AGAIN, 0) unless defined($caches{$id}{$itemid}); +# if ($item == 0) { return ($caches{$id}{$itemid}{'count'}, 1); } +# elsif ($item == 1) { return ($caches{$id}{$itemid}{'age'}, 1); } +# } + return (PM_ERR_PMID, 0); +} + +$pmda = PCP::PMDA->new('memcache', 89); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.pid', '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.uptime', '', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.current_items', '', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.total_items', '', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U64, $memcache_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'memcache.bytes', '', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.current_connections', '', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U32, $memcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'memcache.total_connections', '', ''); +$pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.connection_structures', '', ''); +$pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U32, $memcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'memcache.gets', '', ''); +$pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U32, $memcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'memcache.sets', '', ''); +$pmda->add_metric(pmda_pmid(0,10), PM_TYPE_U32, $memcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'memcache.hits', '', ''); +$pmda->add_metric(pmda_pmid(0,11), PM_TYPE_U32, $memcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'memcache.misses', '', ''); +$pmda->add_metric(pmda_pmid(0,12), PM_TYPE_U64, $memcache_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'memcache.bytes_read', '', ''); +$pmda->add_metric(pmda_pmid(0,13), PM_TYPE_U64, $memcache_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'memcache.bytes_written', '', ''); +$pmda->add_metric(pmda_pmid(0,14), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'memcache.limit_maxbytes', '', ''); +# $id = 0; +# foreach $n (6 .. 17) { # stats slabs (N=6-17) +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), +# "memcache.slabs.slab$n.chunk_size", '', ''); +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), +# "memcache.slabs.slab$n.chunks_per_page", '', ''); +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), +# "memcache.slabs.slab$n.total_pages", '', ''); +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), +# "memcache.slabs.slab$n.total_chunks", '', ''); +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), +# "memcache.slabs.slab$n.used_chunks", '', ''); +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), +# "memcache.slabs.slab$n.free_chunks", '', ''); +# $pmda->add_metric(pmda_pmid(1,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), +# "memcache.slabs.slab$n.free_chunks_end", '', ''); +# } + +$pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'memcache.active_slabs', '', ''); +$pmda->add_metric(pmda_pmid(2,1), PM_TYPE_U32, $memcache_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'memcache.total_malloced', '', ''); + +# $id = 0; +# foreach $n (6 .. 17) { # stats items (N=6-17) +# $pmda->add_metric(pmda_pmid(3,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), +# "memcache.items.item$n.count", '', ''); +# $pmda->add_metric(pmda_pmid(3,$id++), PM_TYPE_U32, $memcache_indom, +# PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), +# "memcache.items.item$n.age", '', ''); +# } + +$pmda->add_indom($memcache_indom, \@memcache_instances, + 'Instance domain exporting each memcache daemon', ''); + +$pmda->add_timer($memcache_delay, \&memcache_timer_callback, 0); +$pmda->set_fetch_callback(\&memcache_fetch_callback); + +&memcache_connect; +&memcache_timer_callback; + +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdamemcache - memcache performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +This PMDA extracts performance data from memcached, a distributed memory +caching daemon commonly used to improve web serving performance. A farm +of memcached processes over multiple servers can be utilised by a single +web application, increasing the total available object cache size, and +decreasing the database load associated with smaller cache sizes. This +system is described in detail at http://www.danga.com/memcached. + +=head1 INSTALLATION + +Configure B to extract the values from set of hosts +used in the memcache farm. These hosts can be listed in the +$PCP_PMDAS_DIR/memcache/memcache.conf file, in the format (i.e. +Perl array) described at the top of pmdamemcache.pl. A custom +refresh rate can also be configured using this mechanism. + + # cd $PCP_PMDAS_DIR/memcache + # [ edit memcache.conf ] + +Once this is setup, you can access the names and values for the +memcache performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/memcache + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/memcache + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/memcache/memcache.conf + +configuration file listing monitored memcache instances + +=item $PCP_PMDAS_DIR/memcache/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/memcache/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/memcache.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and memcached(1). diff --git a/src/pmdas/mmv/GNUmakefile b/src/pmdas/mmv/GNUmakefile new file mode 100644 index 0000000..e674bd1 --- /dev/null +++ b/src/pmdas/mmv/GNUmakefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2009-2010 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mmv +TARGET = mmvdump$(EXECSUFFIX) +CFILES = mmvdump.c + +SUBDIRS = src + +LCFILES = acme.c +LTARGET = acme$(EXECSUFFIX) +LSRCFILES = $(LCFILES) README.demos Makefile.demos +TARGETS = $(TARGET) $(LTARGET) + +LLDFLAGS = -L$(TOPDIR)/src/libpcp_mmv/src -L$(TOPDIR)/src/libpcp/src +LLDLIBS = -lpcp_mmv $(PCPLIB) +LDIRT = mmvdump acme + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +DEMODIR = $(PCP_DEMOS_DIR)/$(IAM) +DEMOFILES = README.demos Makefile.demos +CONF_LINE = "mmv 70 dso mmv_init $(PCP_PMDAS_DIR)/mmv/pmda_mmv.$(DSOSUFFIX)" + +default_pcp default :: $(TARGETS) + +default_pcp default :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +include $(BUILDRULES) + +install_pcp install :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install_pcp install :: $(SUBDIRS) + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(TARGET) $(PMDADIR)/$(TARGET) + $(INSTALL) -m 755 -d $(DEMODIR) + $(INSTALL) -m 644 Makefile.demos $(DEMODIR)/Makefile + $(INSTALL) -m 644 README.demos $(DEMODIR)/README + $(INSTALL) -m 644 $(CFILES) $(LCFILES) $(DEMODIR) + +# check-build only, binary not installed (but source is) +$(LTARGET): acme.c + $(CCF) -o $@ $^ $(LDFLAGS) $(LDLIBS) diff --git a/src/pmdas/mmv/Makefile.demos b/src/pmdas/mmv/Makefile.demos new file mode 100644 index 0000000..5c6341c --- /dev/null +++ b/src/pmdas/mmv/Makefile.demos @@ -0,0 +1,33 @@ +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +SHELL = /bin/sh +CC = cc +CFLAGS = -g + +TARGETS = acme mmvdump + +default: $(TARGETS) + +mmvdump: mmvdump.c + $(CC) $(CFLAGS) -o $@ $^ -lpcp + +acme: acme.c + $(CC) $(CFLAGS) -o $@ $^ -lpcp_mmv -lpcp + +clean: + rm -f *.o + +clobber: clean + rm -f $(TARGETS) diff --git a/src/pmdas/mmv/README.demos b/src/pmdas/mmv/README.demos new file mode 100644 index 0000000..5ed4ef9 --- /dev/null +++ b/src/pmdas/mmv/README.demos @@ -0,0 +1,26 @@ +sample pcp_mmv applications +=========================== + +acme.c + is a sample application that is instrumented with the pcp_mmv +interface to generate metrics available from the MMV PMDA +(Performance Metrics Domain Agent). It runs without exiting, +and continually updates the mmv.acme.* metric values. Detailed +discussion about the workings of this application and each of +the instrumentation API calls it uses, is available as part of +the "Performance Co-Pilot Programmer's Guide". + +mmvdump.c + an example program that dumps the contents of a (memory mapped) +mmv(5) format file, as created by the pcp_mmv library and read by +the MMV PMDA. The binary is also shipped as part of pcp and can +be found installed below ${PCP_PMDAS_DIR}/mmv. + + +All source is shipped as part of pcp as well and is installed in +${PCP_DEMOS_DIR}/mmv. If you have a C toolchain installed, the +sources and Makefile in this directory may be used to create the +functionally equivalent binaries, by entering the command + + % make acme mmvdump + diff --git a/src/pmdas/mmv/acme.c b/src/pmdas/mmv/acme.c new file mode 100644 index 0000000..212ce39 --- /dev/null +++ b/src/pmdas/mmv/acme.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include + +static mmv_instances_t products[] = { + { .internal = 0, .external = "Anvils" }, + { .internal = 1, .external = "Rockets" }, + { .internal = 2, .external = "Giant_Rubber_Bands" }, +}; +#define ACME_PRODUCTS_INDOM 61 +#define ACME_PRODUCTS_COUNT (sizeof(products)/sizeof(products[0])) + +static mmv_indom_t indoms[] = { + { .serial = ACME_PRODUCTS_INDOM, + .count = ACME_PRODUCTS_COUNT, + .instances = products, + .shorttext = "Acme products", + .helptext = "Most popular products produced by the Acme Corporation", + }, +}; + +static mmv_metric_t metrics[] = { + { .name = "products.count", + .item = 7, + .type = MMV_TYPE_U64, + .semantics = MMV_SEM_COUNTER, + .dimension = MMV_UNITS(0,0,1,0,0,PM_COUNT_ONE), + .indom = ACME_PRODUCTS_INDOM, + .shorttext = "Acme factory product throughput", + .helptext = +"Monotonic increasing counter of products produced in the Acme Corporation\n" +"factory since starting the Acme production application. Quality guaranteed.", + }, + { .name = "products.time", + .item = 8, + .type = MMV_TYPE_U64, + .semantics = MMV_SEM_COUNTER, + .dimension = MMV_UNITS(0,1,0,0,PM_TIME_USEC,0), + .indom = ACME_PRODUCTS_INDOM, + .shorttext = "Machine time spent producing Acme products", + .helptext = +"Machine time spent producing Acme Corporation products. Does not include\n" +"time in queues waiting for production machinery.", + }, + { .name = "products.queuetime", + .item = 10, + .type = MMV_TYPE_U64, + .semantics = MMV_SEM_COUNTER, + .dimension = MMV_UNITS(0,1,0,0,PM_TIME_USEC,0), + .indom = ACME_PRODUCTS_INDOM, + .shorttext = "Queued time while producing Acme products", + .helptext = +"Time spent in the queue waiting to build Acme Corporation products,\n" +"while some other Acme product was being built instead of this one.", + }, +}; + +#define INDOM_COUNT (sizeof(indoms)/sizeof(indoms[0])) +#define METRIC_COUNT (sizeof(metrics)/sizeof(metrics[0])) +#define ACME_CLUSTER 321 /* PMID cluster identifier */ + +int +main(int argc, char * argv[]) +{ + void *base; + pmAtomValue *count[ACME_PRODUCTS_COUNT]; + pmAtomValue *machine[ACME_PRODUCTS_COUNT]; + pmAtomValue *inqueue[ACME_PRODUCTS_COUNT]; + unsigned int working; + unsigned int product; + unsigned int i; + + base = mmv_stats_init("acme", ACME_CLUSTER, 0, + metrics, METRIC_COUNT, indoms, INDOM_COUNT); + if (!base) { + perror("mmv_stats_init"); + return 1; + } + + for (i = 0; i < ACME_PRODUCTS_COUNT; i++) { + count[i] = mmv_lookup_value_desc(base, + "products.count", products[i].external); + machine[i] = mmv_lookup_value_desc(base, + "products.time", products[i].external); + inqueue[i] = mmv_lookup_value_desc(base, + "products.queuetime", products[i].external); + } + + while (1) { + /* choose a random number between 0-N -> product */ + product = rand() % ACME_PRODUCTS_COUNT; + + /* assign a time spent "working" on this product */ + working = rand() % 50000; + + /* pretend to "work" so process doesn't burn CPU */ + usleep(working); + + /* update the memory mapped values for this one: */ + /* one more product produced and work time spent */ + mmv_inc_value(base, machine[product], working); /* API */ + count[product]->ull += 1; /* or direct mmap update */ + + /* all other products are "queued" for this time */ + for (i = 0; i < ACME_PRODUCTS_COUNT; i++) + if (i != product) + mmv_inc_value(base, inqueue[i], working); + } + + mmv_stats_stop("acme", base); + return 0; +} diff --git a/src/pmdas/mmv/mmvdump.c b/src/pmdas/mmv/mmvdump.c new file mode 100644 index 0000000..0943618 --- /dev/null +++ b/src/pmdas/mmv/mmvdump.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2013 Red Hat. + * Copyright (C) 2009 Aconex. All Rights Reserved. + * Copyright (C) 2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include + +void +dump_indoms(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_string_t * string; + mmv_disk_indom_t * indom = (mmv_disk_indom_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, indoms offset %" PRIu64 " (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + __uint64_t off = offset + i * sizeof(mmv_disk_indom_t); + printf(" [%u/%"PRIi64"] %d instances, starting at offset %"PRIi64"\n", + indom[i].serial, off, indom[i].count, indom[i].offset); + if (indom[i].shorttext) { + string = (mmv_disk_string_t *) + ((char *)addr + indom[i].shorttext); + printf(" shorttext=%s\n", string->payload); + } + else + printf(" (no shorttext)\n"); + if (indom[i].helptext) { + string = (mmv_disk_string_t *) + ((char *)addr + indom[i].helptext); + printf(" helptext=%s\n", string->payload); + } + else + printf(" (no helptext)\n"); + } +} + +void +dump_insts(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_instance_t * inst = (mmv_disk_instance_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, instances offset %"PRIi64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + mmv_disk_indom_t * indom = (mmv_disk_indom_t *) + ((char *)addr + inst[i].indom); + printf(" [%u/%"PRIi64"] instance = [%d or \"%s\"]\n", + indom->serial, + (offset + i * sizeof(mmv_disk_instance_t)), + inst[i].internal, inst[i].external); + } +} + +static char * +metrictype(int mtype) +{ + char *type; + + switch (mtype) { + case MMV_TYPE_I32: + type = "32-bit int"; + break; + case MMV_TYPE_U32: + type = "32-bit unsigned int"; + break; + case MMV_TYPE_I64: + type = "64-bit int"; + break; + case MMV_TYPE_U64: + type = "64-bit unsigned int"; + break; + case MMV_TYPE_FLOAT: + type = "float"; + break; + case MMV_TYPE_DOUBLE: + type = "double"; + break; + case MMV_TYPE_STRING: + type = "string"; + break; + case MMV_TYPE_ELAPSED: + type = "elapsed"; + break; + default: + type = "?"; + break; + } + return type; +} + +static char * +metricsem(int msem) +{ + char *sem; + + switch (msem) { + case PM_SEM_COUNTER: + sem = "counter"; + break; + case PM_SEM_INSTANT: + sem = "instant"; + break; + case PM_SEM_DISCRETE: + sem = "discrete"; + break; + default: + sem = "?"; + break; + } + return sem; +} + +void +dump_metrics(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_string_t * string; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: toc offset %ld, metrics offset %"PRIi64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + __uint64_t off = offset + i * sizeof(mmv_disk_metric_t); + printf(" [%u/%"PRIi64"] %s\n", m[i].item, off, m[i].name); + printf(" type=%s (0x%x), sem=%s (0x%x), pad=0x%x\n", + metrictype(m[i].type), m[i].type, + metricsem(m[i].semantics), m[i].semantics, + m[i].padding); + printf(" units=%s\n", pmUnitsStr(&m[i].dimension)); + if (m[i].indom != PM_INDOM_NULL && m[i].indom != 0) + printf(" indom=%d\n", m[i].indom); + else + printf(" (no indom)\n"); + if (m[i].shorttext) { + string = (mmv_disk_string_t *) + ((char *)addr + m[i].shorttext); + printf(" shorttext=%s\n", string->payload); + } + else + printf(" (no shorttext)\n"); + if (m[i].helptext) { + string = (mmv_disk_string_t *) + ((char *)addr + m[i].helptext); + printf(" helptext=%s\n", string->payload); + } + else + printf(" (no helptext)\n"); + } +} + +void +dump_values(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_value_t * vals = (mmv_disk_value_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, values offset %"PRIu64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + mmv_disk_string_t * string; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + vals[i].metric); + __uint64_t off = offset + i * sizeof(mmv_disk_value_t); + + printf(" [%u/%"PRIu64"] %s", m->item, off, m->name); + if (m->indom && m->indom != PM_IN_NULL) { + mmv_disk_instance_t *indom = (mmv_disk_instance_t *) + ((char *)addr + vals[i].instance); + printf("[%d or \"%s\"]", + indom->internal, indom->external); + } + + switch (m->type) { + case MMV_TYPE_I32: + printf(" = %d", vals[i].value.l); + break; + case MMV_TYPE_U32: + printf(" = %u", vals[i].value.ul); + break; + case MMV_TYPE_I64: + printf(" = %" PRIi64, vals[i].value.ll); + break; + case MMV_TYPE_U64: + printf(" = %" PRIu64, vals[i].value.ull); + break; + case MMV_TYPE_FLOAT: + printf(" = %f", vals[i].value.f); + break; + case MMV_TYPE_DOUBLE: + printf(" = %lf", vals[i].value.d); + break; + case MMV_TYPE_STRING: + string = (mmv_disk_string_t *)((char *)addr + vals[i].extra); + printf(" = \"%s\"", string->payload); + break; + case MMV_TYPE_ELAPSED: { + struct timeval tv; + __int64_t t; + + __pmtimevalNow(&tv); + t = vals[i].value.ll; + if (vals[i].extra < 0) + t += ((tv.tv_sec*1e6 + tv.tv_usec) + vals[i].extra); + printf(" = %"PRIi64" (value=%"PRIi64"/extra=%"PRIi64")", + t, vals[i].value.ll, vals[i].extra); + if (vals[i].extra > 0) + printf("Bad ELAPSED 'extra' value found!"); + break; + } + default: + printf("Unknown type %d", m->type); + } + putchar('\n'); + } +} + +void +dump_strings(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_string_t * string = (mmv_disk_string_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, string offset %"PRIu64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + printf(" [%u/%"PRIu64"] %s\n", + i+1, offset + i * sizeof(mmv_disk_string_t), + string[i].payload); + } +} + +int +dump(const char *file, void *addr) +{ + int i; + mmv_disk_header_t * hdr = (mmv_disk_header_t *) addr; + mmv_disk_toc_t * toc = (mmv_disk_toc_t *) + ((char *)addr + sizeof(mmv_disk_header_t)); + + if (strcmp(hdr->magic, "MMV")) { + printf("Bad magic: %c%c%c\n", + hdr->magic[0], hdr->magic[1], hdr->magic[2]); + return 1; + } + if (hdr->version != MMV_VERSION) { + printf("version %d not supported\n", hdr->version); + return 1; + } + + printf("MMV file = %s\n", file); + printf("Version = %d\n", hdr->version); + printf("Generated = %"PRIu64"\n", hdr->g1); + if (hdr->g1 != hdr->g2) { + printf("Generated2 = %"PRIu64"\n", hdr->g2); + printf("Mismatched generation numbers\n"); + return 1; + } + printf("TOC count = %u\n", hdr->tocs); + printf("Cluster = %u\n", hdr->cluster); + printf("Process = %d\n", hdr->process); + printf("Flags = 0x%x\n", hdr->flags); + + for (i = 0; i < hdr->tocs; i++) { + __uint64_t base = ((char *)&toc[i] - (char *)addr); + switch (toc[i].type) { + case MMV_TOC_INDOMS: + dump_indoms(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_INSTANCES: + dump_insts(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_VALUES: + dump_values(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_METRICS: + dump_metrics(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_STRINGS: + dump_strings(addr, i, base, toc[i].offset, toc[i].count); + break; + default: + printf("Unrecognised TOC[%d] type: 0x%x\n", i, toc[i].type); + } + } + return 0; +} + +int +main(int argc, char * argv[]) +{ + int fd; + char file[MAXPATHLEN]; + + if (argc > 2) { + printf("USAGE: %s \n", argv[0]); + exit(1); + } + if (argc > 1) + strncpy(file, argv[1], MAXPATHLEN); + else + snprintf(file, MAXPATHLEN, "%s%cmmv%ctest", + pmGetConfig("PCP_TMP_DIR"), + __pmPathSeparator(), __pmPathSeparator()); + file[MAXPATHLEN-1] = '\0'; + + if ((fd = open(file, O_RDONLY)) < 0) + perror(file); + else { + struct stat s; + void * addr; + + if (fstat(fd, &s) < 0) + perror(file); + else if ((addr = __pmMemoryMap(fd, s.st_size, 0)) != NULL) + return dump(file, addr); + } + return 1; +} diff --git a/src/pmdas/mmv/src/GNUmakefile b/src/pmdas/mmv/src/GNUmakefile new file mode 100644 index 0000000..421c38b --- /dev/null +++ b/src/pmdas/mmv/src/GNUmakefile @@ -0,0 +1,59 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2009-2010 Aconex. All Rights Reserved. +# Copyright (c) 2000-2001,2009 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mmv +DOMAIN = MMV +CMDTARGET = pmda$(IAM)$(EXECSUFFIX) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +PMDAINIT = $(IAM)_init +TARGETS = $(CMDTARGET) $(LIBTARGET) + +CFILES = mmv.c +VERSION_SCRIPT = exports +LSRCFILES = Install Remove root_mmv +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) +LDIRT = domain.h *.log pmns $(VERSION_SCRIPT) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +default_pcp default: $(TARGETS) pmns + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h + $(INSTALL) -m 755 $(TARGETS) Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmns $(PMDADIR)/root_mmv + $(INSTALL) -m 644 root_mmv $(PCP_VAR_DIR)/pmns/root_mmv + +$(CMDTARGET): $(OBJECTS) + +$(IAM).o : domain.h +$(LIBTARGET) : $(VERSION_SCRIPT) + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +pmns : + $(LN_S) -f root_mmv pmns diff --git a/src/pmdas/mmv/src/Install b/src/pmdas/mmv/src/Install new file mode 100755 index 0000000..d8f148b --- /dev/null +++ b/src/pmdas/mmv/src/Install @@ -0,0 +1,36 @@ +#! /bin/sh +# +# Copyright (C) 1997,2009 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (C) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mmv +dso_opt=true +socket_opt=true +socket_inet_def=2081 +forced_restart=false +pmda_interface=4 +pmns_source=root_mmv + +if [ ! -e "$PCP_TMP_DIR/mmv" ] +then + echo "creating $PCP_TMP_DIR/mmv" + mkdir -p -m 1777 "$PCP_TMP_DIR/mmv" +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/mmv/src/Remove b/src/pmdas/mmv/src/Remove new file mode 100755 index 0000000..a463371 --- /dev/null +++ b/src/pmdas/mmv/src/Remove @@ -0,0 +1,23 @@ +#! /bin/sh +# +# Copyright (C) 1997,2009 Silicon Graphics, Inc., All Rights Reserved. +# Copyright (C) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mmv +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/mmv/src/mmv.c b/src/pmdas/mmv/src/mmv.c new file mode 100644 index 0000000..ecc97ed --- /dev/null +++ b/src/pmdas/mmv/src/mmv.c @@ -0,0 +1,915 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2009-2010 Aconex. All Rights Reserved. + * Copyright (c) 1995-2000,2009 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * + * MMV PMDA + * + * This PMDA uses specially formatted files in either /var/tmp/mmv or some + * other directory, as specified on the command line. Each file represents + * a separate "cluster" of values with flat name structure for each cluster. + * Names for the metrics are optionally prepended with mmv and then the name + * of the file (by default - this can be changed). + */ + +#include "pmapi.h" +#include "mmv_stats.h" +#include "mmv_dev.h" +#include "impl.h" +#include "pmda.h" +#include "./domain.h" +#include +#include + +static int isDSO = 1; +static char *username; + +/* command line option handling - both short and long options */ +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; +static pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +static pmdaMetric * metrics; +static int mcnt; +static pmdaIndom * indoms; +static int incnt; + +static int reload; +static __pmnsTree * pmns; +static int statsdir_code; /* last statsdir stat code */ +static time_t statsdir_ts; /* last statsdir timestamp */ +static char * prefix = "mmv"; + +static char * pcptmpdir; /* probably /var/tmp */ +static char * pcpvardir; /* probably /var/pcp */ +static char * pcppmdasdir; /* probably /var/pcp/pmdas */ +static char pmnsdir[MAXPATHLEN]; /* pcpvardir/pmns */ +static char statsdir[MAXPATHLEN]; /* pcptmpdir/ */ + +typedef struct { + char * name; /* strdup client name */ + void * addr; /* mmap */ + mmv_disk_value_t * values; /* values in mmap */ + mmv_disk_metric_t * metrics; /* metric descs in mmap */ + int vcnt; /* number of values */ + int mcnt; /* number of metrics */ + pid_t pid; /* process identifier */ + int cluster; /* cluster identifier */ + __int64_t len; /* mmap region len */ + __uint64_t gen; /* generation number on open */ +} stats_t; + +static stats_t * slist; +static int scnt; + +/* + * Choose an unused cluster ID while honouring specific requests. + * If a specific (non-zero) cluster is requested we always use it. + */ +static int +choose_cluster(int requested, const char *path) +{ + int i; + + if (!requested) { + int next_cluster = 1; + + for (i = 0; i < scnt; i++) { + if (slist[i].cluster == next_cluster) { + next_cluster++; + i = 0; /* restart, we're filling holes */ + } + } + return next_cluster; + } + + for (i = 0; i < scnt; i++) { + if (slist[i].cluster == requested) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, + "MMV: %s: duplicate cluster %d in use", + pmProgname, requested); + break; + } + } + return requested; +} + +static int +create_client_stat(const char *client, const char *path, size_t size) +{ + int fd; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: create_client_stat: %s, %s", client, path); + + if ((fd = open(path, O_RDONLY)) >= 0) { + void *m = __pmMemoryMap(fd, size, 0); + + close(fd); + if (m != NULL) { + mmv_disk_header_t * hdr = (mmv_disk_header_t *)m; + int cluster; + + if (strncmp(hdr->magic, "MMV", 4)) { + __pmMemoryUnmap(m, size); + return -EINVAL; + } + + if (hdr->version != MMV_VERSION) { + __pmNotifyErr(LOG_ERR, "%s: %s client version %d " + "not supported (current is %d)", + pmProgname, prefix, hdr->version, MMV_VERSION); + __pmMemoryUnmap(m, size); + return -ENOSYS; + } + + if (!hdr->g1 || hdr->g1 != hdr->g2) { + /* still in flux, wait till next time */ + __pmMemoryUnmap(m, size); + return -EAGAIN; + } + + /* optionally verify the creator PID is running */ + if (hdr->process && (hdr->flags & MMV_FLAG_PROCESS) && + !__pmProcessExists((pid_t)hdr->process)) { + __pmMemoryUnmap(m, size); + return -ESRCH; + } + + /* all checks out, we'll use this one */ + cluster = choose_cluster(hdr->cluster, path); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: %s: loading %s client: %d \"%s\"", + pmProgname, prefix, cluster, path); + + slist = realloc(slist, sizeof(stats_t)*(scnt+1)); + if (slist != NULL) { + slist[scnt].name = strdup(client); + slist[scnt].addr = m; + slist[scnt].pid = (pid_t)((hdr->flags & MMV_FLAG_PROCESS)? hdr->process : 0); + slist[scnt].cluster = cluster; + slist[scnt].mcnt = 0; + slist[scnt].gen = hdr->g1; + slist[scnt].len = size; + scnt++; + } else { + __pmNotifyErr(LOG_ERR, "%s: client \"%s\" out of memory - %s", + pmProgname, client, osstrerror()); + __pmMemoryUnmap(m, size); + scnt = 0; + } + } else { + __pmNotifyErr(LOG_ERR, "%s: failed to memory map \"%s\" - %s", + pmProgname, path, osstrerror()); + } + } else { + __pmNotifyErr(LOG_ERR, "%s: failed to open client file \"%s\" - %s", + pmProgname, client, osstrerror()); + } + return 0; +} + +/* check validity of client metric name, return non-zero if bad or duplicate */ +static int +verify_metric_name(const char *name, int pos, stats_t *s) +{ + const char *p = name; + pmID pmid; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: verify_metric_name: %s", name); + + if (p == NULL || *p == '\0' || !isalpha((int)*p)) { + __pmNotifyErr(LOG_WARNING, "Invalid metric[%d] name start in %s, ignored", + pos, s->name); + return -EINVAL; + } + for (++p; (p != NULL && *p != '\0'); p++) { + if (isalnum((int)*p) || *p == '_' || *p == '.') + continue; + __pmNotifyErr(LOG_WARNING, "invalid metric[%d] name in %s (@%c), ignored", + pos, s->name, *p); + return -EINVAL; + } + if (pmdaTreePMID(pmns, name, &pmid) == 0) + return -EEXIST; + return 0; +} + +/* check client item number validity - must not be too large to fit in PMID! */ +static int +verify_metric_item(unsigned int item, char *name, stats_t *s) +{ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: verify_metric_item: %u - %s", item, name); + + if (pmid_item(item) != item) { + __pmNotifyErr(LOG_WARNING, "invalid item %u (%s) in %s, ignored", + item, name, s->name); + return -EINVAL; + } + return 0; +} + +static int +create_metric(pmdaExt *pmda, stats_t *s, mmv_disk_metric_t *m, char *name, pmID pmid) +{ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: create_metric: %s - %s", name, pmIDStr(pmid)); + + metrics = realloc(metrics, sizeof(pmdaMetric) * (mcnt + 1)); + if (metrics == NULL) { + __pmNotifyErr(LOG_ERR, "cannot grow MMV metric list: %s", s->name); + return -ENOMEM; + } + + metrics[mcnt].m_user = NULL; + metrics[mcnt].m_desc.pmid = pmid; + + if (m->type == MMV_TYPE_ELAPSED) { + pmUnits unit = PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0); + metrics[mcnt].m_desc.sem = PM_SEM_COUNTER; + metrics[mcnt].m_desc.type = MMV_TYPE_I64; + metrics[mcnt].m_desc.units = unit; + } else { + if (m->semantics) + metrics[mcnt].m_desc.sem = m->semantics; + else + metrics[mcnt].m_desc.sem = PM_SEM_COUNTER; + metrics[mcnt].m_desc.type = m->type; + memcpy(&metrics[mcnt].m_desc.units, &m->dimension, sizeof(pmUnits)); + } + metrics[mcnt].m_desc.indom = (!m->indom || m->indom == PM_INDOM_NULL) ? + PM_INDOM_NULL : pmInDom_build(pmda->e_domain, + (s->cluster << 11) | m->indom); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: map_stats adding metric[%d] %s %s from %s\n", + mcnt, name, pmIDStr(pmid), s->name); + + mcnt++; + __pmAddPMNSNode(pmns, pmid, name); + + return 0; +} + +/* check client serial number validity, and check for a duplicate */ +static int +verify_indom_serial(pmdaExt *pmda, int serial, stats_t *s, pmInDom *p, pmdaIndom **i) +{ + int index; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: verify_indom_serial: %u", serial); + + if (pmInDom_serial(serial) != serial) { + __pmNotifyErr(LOG_WARNING, "invalid serial %u in %s, ignored", + serial, s->name); + return -EINVAL; + } + + *p = pmInDom_build(pmda->e_domain, (s->cluster << 11) | serial); + for (index = 0; index < incnt; index++) { + *i = &indoms[index]; + if (indoms[index].it_indom == *p) + return -EEXIST; + } + *i = NULL; + return 0; +} + +static int +update_indom(pmdaExt *pmda, stats_t *s, mmv_disk_indom_t *id, pmdaIndom *ip) +{ + int i, j, size, newinsts = 0; + mmv_disk_instance_t *in = (mmv_disk_instance_t *)((char *)s->addr + id->offset); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: update_indom: %u (%d insts)", + id->serial, ip->it_numinst); + + /* first calculate how many new instances, so we know what to alloc */ + for (i = 0; i < id->count; i++) { + for (j = 0; j < ip->it_numinst; j++) + if (ip->it_set[j].i_inst == in[i].internal) + continue; + if (j == ip->it_numinst) + newinsts++; + } + + if (!newinsts) + return 0; + + /* allocate memory, then append new instances to the known set */ + size = sizeof(pmdaInstid) * (ip->it_numinst + newinsts); + ip->it_set = (pmdaInstid *)realloc(ip->it_set, size); + if (ip->it_set != NULL) { + for (i = 0; i < id->count; i++) { + for (j = 0; j < ip->it_numinst; j++) + if (ip->it_set[j].i_inst == in[j].internal) + continue; + if (j == ip->it_numinst) { + ip->it_set[j].i_inst = in[i].internal; + ip->it_set[j].i_name = in[i].external; + ip->it_numinst++; + } + } + } else { + __pmNotifyErr(LOG_ERR, "%s: cannot get memory for instance list in %s", + pmProgname, s->name); + ip->it_numinst = 0; + return -ENOMEM; + } + return 0; +} + +static int +create_indom(pmdaExt *pmda, stats_t *s, mmv_disk_indom_t *id, pmInDom indom) +{ + int i; + pmdaIndom *ip; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: create_indom: %u", id->serial); + + indoms = realloc(indoms, sizeof(pmdaIndom) * (incnt + 1)); + if (indoms == NULL) { + __pmNotifyErr(LOG_ERR, "%s: cannot grow indom list in %s", + pmProgname, s->name); + return -ENOMEM; + } + ip = &indoms[incnt++]; + ip->it_indom = indom; + ip->it_set = (pmdaInstid *)calloc(id->count, sizeof(pmdaInstid)); + if (ip->it_set != NULL) { + mmv_disk_instance_t * in = (mmv_disk_instance_t *) + ((char *)s->addr + id->offset); + ip->it_numinst = id->count; + for (i = 0; i < ip->it_numinst; i++) { + ip->it_set[i].i_inst = in[i].internal; + ip->it_set[i].i_name = in[i].external; + } + } else { + __pmNotifyErr(LOG_ERR, "%s: cannot get memory for instance list in %s", + pmProgname, s->name); + ip->it_numinst = 0; + return -ENOMEM; + } + return 0; +} + +static void +map_stats(pmdaExt *pmda) +{ + struct dirent **files; + char name[64]; + int need_reload = 0; + int i, j, k, sts, num; + + if (pmns) + __pmFreePMNS(pmns); + + if ((sts = __pmNewPMNS(&pmns)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create new pmns: %s\n", + pmProgname, pmErrStr(sts)); + pmns = NULL; + return; + } + + /* hard-coded metrics (not from mmap'd files */ + snprintf(name, sizeof(name), "%s.reload", prefix); + __pmAddPMNSNode(pmns, pmid_build(pmda->e_domain, 0, 0), name); + snprintf(name, sizeof(name), "%s.debug", prefix); + __pmAddPMNSNode(pmns, pmid_build(pmda->e_domain, 0, 1), name); + mcnt = 2; + + if (indoms != NULL) { + for (i = 0; i < incnt; i++) + free(indoms[i].it_set); + free(indoms); + indoms = NULL; + incnt = 0; + } + + if (slist != NULL) { + for (i = 0; i < scnt; i++) { + free(slist[i].name); + __pmMemoryUnmap(slist[i].addr, slist[i].len); + } + free(slist); + slist = NULL; + scnt = 0; + } + + num = scandir(statsdir, &files, NULL, NULL); + for (i = 0; i < num; i++) { + struct stat statbuf; + char path[MAXPATHLEN]; + char *client; + + if (files[i]->d_name[0] == '.') + continue; + + client = files[i]->d_name; + sprintf(path, "%s%c%s", statsdir, __pmPathSeparator(), client); + + if (stat(path, &statbuf) >= 0 && S_ISREG(statbuf.st_mode)) + if (create_client_stat(client, path, statbuf.st_size) == -EAGAIN) + need_reload = 1; + } + + for (i = 0; i < num; i++) + free(files[i]); + if (num > 0) + free(files); + + for (i = 0; slist && i < scnt; i++) { + stats_t * s = slist + i; + mmv_disk_header_t * hdr = (mmv_disk_header_t *)s->addr; + mmv_disk_toc_t * toc = (mmv_disk_toc_t *) + ((char *)s->addr + sizeof(mmv_disk_header_t)); + + for (j = 0; j < hdr->tocs; j++) { + switch (toc[j].type) { + case MMV_TOC_METRICS: { + mmv_disk_metric_t *ml = (mmv_disk_metric_t *) + ((char *)s->addr + toc[j].offset); + + s->metrics = ml; + s->mcnt = toc[j].count; + + for (k = 0; k < toc[j].count; k++) { + char name[MAXPATHLEN]; + pmID pmid; + + /* build name, check its legitimate and unique */ + if (hdr->flags & MMV_FLAG_NOPREFIX) + sprintf(name, "%s.", prefix); + else + sprintf(name, "%s.%s.", prefix, s->name); + strcat(name, ml[k].name); + if (verify_metric_name(name, k, s) != 0) + continue; + if (verify_metric_item(ml[k].item, name, s) != 0) + continue; + + pmid = pmid_build(pmda->e_domain, s->cluster, ml[k].item); + create_metric(pmda, s, &ml[k], name, pmid); + } + break; + } + + case MMV_TOC_INDOMS: { + mmv_disk_indom_t * id = (mmv_disk_indom_t *) + ((char *)s->addr + toc[j].offset); + + for (k = 0; k < toc[j].count; k++) { + int sts, serial = id[k].serial; + pmInDom pmindom; + pmdaIndom *ip; + + sts = verify_indom_serial(pmda, serial, s, &pmindom, &ip); + if (sts == -EINVAL) + continue; + else if (sts == -EEXIST) + /* see if we have new instances to add here */ + update_indom(pmda, s, &id[k], ip); + else + /* first time we've observed this indom */ + create_indom(pmda, s, &id[k], pmindom); + } + break; + } + + case MMV_TOC_VALUES: { + s->vcnt = toc[j].count; + s->values = (mmv_disk_value_t *) + ((char *)s->addr + toc[j].offset); + break; + } + + default: + break; + } + } + } + + pmdaTreeRebuildHash(pmns, mcnt); /* for reverse (pmid->name) lookups */ + reload = need_reload; +} + +static int +mmv_lookup_stat_metric_value(pmID pmid, unsigned int inst, + stats_t **sout, mmv_disk_metric_t **mout, mmv_disk_value_t **vout) +{ + __pmID_int * id = (__pmID_int *)&pmid; + mmv_disk_metric_t * m; + mmv_disk_value_t * v; + stats_t * s; + int si, mi, vi; + int sts = PM_ERR_PMID; + + for (si = 0; si < scnt; si++) { + s = &slist[si]; + if (s->cluster != id->cluster) + continue; + + m = s->metrics; + for (mi = 0; mi < s->mcnt; mi++) { + if (m[mi].item != id->item) + continue; + + sts = PM_ERR_INST; + v = s->values; + for (vi = 0; vi < s->vcnt; vi++) { + mmv_disk_metric_t * mt = (mmv_disk_metric_t *) + ((char *)s->addr + v[vi].metric); + mmv_disk_instance_t * is = (mmv_disk_instance_t *) + ((char *)s->addr + v[vi].instance); + + if ((mt == &m[mi]) && + (mt->indom == PM_INDOM_NULL || mt->indom == 0 || + inst == PM_IN_NULL || is->internal == inst)) { + *sout = s; + *mout = &m[mi]; + *vout = &v[vi]; + return 0; + } + } + } + } + return sts; +} + +/* + * callback provided to pmdaFetch + */ +static int +mmv_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int * id = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (id->cluster == 0) { + if (id->item == 0) { + atom->l = reload; + return 1; + } + if (id->item == 1) { + atom->l = pmDebug; + return 1; + } + return PM_ERR_PMID; + + } else if (scnt > 0) { /* We have at least one source of metrics */ + mmv_disk_string_t * str; + mmv_disk_metric_t * m; + mmv_disk_value_t * v; + stats_t * s; + int rv; + + rv = mmv_lookup_stat_metric_value(mdesc->m_desc.pmid, inst, &s, &m, &v); + if (rv < 0) + return rv; + + switch (m->type) { + case MMV_TYPE_I32: + case MMV_TYPE_U32: + case MMV_TYPE_I64: + case MMV_TYPE_U64: + case MMV_TYPE_FLOAT: + case MMV_TYPE_DOUBLE: + memcpy(atom, &v->value, sizeof(pmAtomValue)); + break; + case MMV_TYPE_ELAPSED: { + atom->ll = v->value.ll; + if (v->extra < 0) { /* inside a timed section */ + struct timeval tv; + __pmtimevalNow(&tv); + atom->ll += (tv.tv_sec * 1e6 + tv.tv_usec) + v->extra; + } + break; + } + case MMV_TYPE_STRING: { + str = (mmv_disk_string_t *)((char *)s->addr + v->extra); + atom->cp = str->payload; + break; + } + case MMV_TYPE_NOSUPPORT: + return PM_ERR_APPVERSION; + } + return 1; + } + + return 0; +} + +static void +mmv_reload_maybe(pmdaExt *pmda) +{ + int i; + struct stat s; + int need_reload = reload; + + /* check if generation numbers changed or monitored process exited */ + for (i = 0; i < scnt; i++) { + mmv_disk_header_t *hdr = (mmv_disk_header_t *)slist[i].addr; + if (hdr->g1 != slist[i].gen || hdr->g2 != slist[i].gen) { + need_reload++; + break; + } + if (slist[i].pid && !__pmProcessExists(slist[i].pid)) { + need_reload++; + break; + } + } + + /* + * check if the directory has been modified, reload if so; + * note modification may involve removal or newly appeared, + * a change in permissions from accessible to not (or vice- + * versa), and so on. + */ + if (stat(statsdir, &s) >= 0) { + if (s.st_mtime != statsdir_ts) { + need_reload++; + statsdir_code = 0; + statsdir_ts = s.st_mtime; + } + } else { + i = oserror(); + if (statsdir_code != i) { + statsdir_code = i; + statsdir_ts = 0; + need_reload++; + } + } + + if (need_reload) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: %s: reloading", pmProgname); + map_stats(pmda); + + pmda->e_indoms = indoms; + pmda->e_nindoms = incnt; + pmdaRehash(pmda, metrics, mcnt); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, + "MMV: %s: %d metrics and %d indoms after reload", + pmProgname, mcnt, incnt); + } +} + +/* Intercept request for descriptor and check if we'd have to reload */ +static int +mmv_desc(pmID pmid, pmDesc *desc, pmdaExt *ep) +{ + mmv_reload_maybe(ep); + return pmdaDesc(pmid, desc, ep); +} + +static int +mmv_text(int ident, int type, char **buffer, pmdaExt *ep) +{ + if (type & PM_TEXT_INDOM) + return PM_ERR_TEXT; + + mmv_reload_maybe(ep); + if (pmid_cluster(ident) == 0) { + if (pmid_item(ident) == 0) { + static char reloadoneline[] = "Control maps reloading"; + static char reloadtext[] = +"Writing anything other then 0 to this metric will result in\n" +"re-reading directory and re-mapping files.\n"; + + *buffer = (type & PM_TEXT_ONELINE) ? reloadoneline : reloadtext; + return 0; + } + else if (pmid_item(ident) == 1) { + static char debugoneline[] = "Debug flag"; + static char debugtext[] = +"See pmdbg(1). pmstore into this metric to change the debug value.\n"; + + *buffer = (type & PM_TEXT_ONELINE) ? debugoneline : debugtext; + return 0; + } + else + return PM_ERR_PMID; + } + else { + mmv_disk_string_t * str; + mmv_disk_metric_t * m; + mmv_disk_value_t * v; + stats_t * s; + + if (mmv_lookup_stat_metric_value(ident, PM_IN_NULL, &s, &m, &v) != 0) + return PM_ERR_PMID; + + if ((type & PM_TEXT_ONELINE) && m->shorttext) { + str = (mmv_disk_string_t *)((char *)s->addr + m->shorttext); + *buffer = str->payload; + return 0; + } + if ((type & PM_TEXT_HELP) && m->helptext) { + str = (mmv_disk_string_t *)((char *)s->addr + m->helptext); + *buffer = str->payload; + return 0; + } + } + + return PM_ERR_TEXT; +} + +static int +mmv_instance(pmInDom indom, int inst, char *name, + __pmInResult **result, pmdaExt *ep) +{ + mmv_reload_maybe(ep); + return pmdaInstance(indom, inst, name, result, ep); +} + +static int +mmv_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +mmv_store(pmResult *result, pmdaExt *ep) +{ + int i, m; + + mmv_reload_maybe(ep); + + for (i = 0; i < result->numpmid; i++) { + pmValueSet * vsp = result->vset[i]; + __pmID_int * id = (__pmID_int *)&vsp->pmid; + + if (id->cluster == 0) { + for (m = 0; m < mcnt; m++) { + __pmID_int * mid = (__pmID_int *)&(metrics[m].m_desc.pmid); + + if (mid->cluster == 0 && mid->item == id->item) { + pmAtomValue atom; + int sts; + + if (vsp->numval != 1 ) + return PM_ERR_CONV; + + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_32, &atom, PM_TYPE_32)) < 0) + return sts; + if (id->item == 0) + reload = atom.l; + else if (id->item == 1) + pmDebug = atom.l; + else + return PM_ERR_PERMISSION; + } + } + } + else + return PM_ERR_PERMISSION; + } + return 0; +} + +static int +mmv_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaTreePMID(pmns, name, pmid); +} + +static int +mmv_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaTreeName(pmns, pmid, nameset); +} + +static int +mmv_children(const char *name, int traverse, char ***kids, int **sts, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaTreeChildren(pmns, name, traverse, kids, sts); +} + +void +__PMDA_INIT_CALL +mmv_init(pmdaInterface *dp) +{ + int m; + int sep = __pmPathSeparator(); + + if (isDSO) { + pmdaDSO(dp, PMDA_INTERFACE_4, "mmv", NULL); + } else { + __pmSetProcessIdentity(username); + } + + pcptmpdir = pmGetConfig("PCP_TMP_DIR"); + pcpvardir = pmGetConfig("PCP_VAR_DIR"); + pcppmdasdir = pmGetConfig("PCP_PMDAS_DIR"); + + snprintf(statsdir, sizeof(statsdir), "%s%c%s", pcptmpdir, sep, prefix); + snprintf(pmnsdir, sizeof(pmnsdir), "%s%c" "pmns", pcpvardir, sep); + statsdir[sizeof(statsdir)-1] = '\0'; + pmnsdir[sizeof(pmnsdir)-1] = '\0'; + + /* Initialize internal dispatch table */ + if (dp->status == 0) { + /* + * number of hard-coded metrics here has to match initializer + * cases below, and pmns initialization in map_stats() + */ + mcnt = 2; + if ((metrics = malloc(mcnt*sizeof(pmdaMetric))) != NULL) { + /* + * all the hard-coded metrics have the same semantics + */ + for (m = 0; m < mcnt; m++) { + if (m == 0) + metrics[m].m_user = &reload; + else if (m == 1) + metrics[m].m_user = &pmDebug; + metrics[m].m_desc.pmid = pmid_build(dp->domain, 0, m); + metrics[m].m_desc.type = PM_TYPE_32; + metrics[m].m_desc.indom = PM_INDOM_NULL; + metrics[m].m_desc.sem = PM_SEM_INSTANT; + memset(&metrics[m].m_desc.units, 0, sizeof(pmUnits)); + } + } else { + __pmNotifyErr(LOG_ERR, "%s: pmdaInit - out of memory\n", + pmProgname); + if (isDSO) + return; + exit(0); + } + + dp->version.four.fetch = mmv_fetch; + dp->version.four.store = mmv_store; + dp->version.four.desc = mmv_desc; + dp->version.four.text = mmv_text; + dp->version.four.instance = mmv_instance; + dp->version.four.pmid = mmv_pmid; + dp->version.four.name = mmv_name; + dp->version.four.children = mmv_children; + pmdaSetFetchCallBack(dp, mmv_fetchCallBack); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, indoms, incnt, metrics, mcnt); + } +} + +int +main(int argc, char **argv) +{ + char logfile[32]; + pmdaInterface dispatch = { 0 }; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + if (strncmp(pmProgname, "pmda", 4) == 0 && strlen(pmProgname) > 4) + prefix = pmProgname + 4; + snprintf(logfile, sizeof(logfile), "%s.log", prefix); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, MMV, logfile, NULL); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + mmv_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/mmv/src/root_mmv b/src/pmdas/mmv/src/root_mmv new file mode 100644 index 0000000..28c810a --- /dev/null +++ b/src/pmdas/mmv/src/root_mmv @@ -0,0 +1,13 @@ +/* + * MMV metrics name space + */ + +#ifndef MMV +#define MMV 70 +#endif + +root { + mmv MMV:*:* +} + +#undef MMV diff --git a/src/pmdas/mounts/GNUmakefile b/src/pmdas/mounts/GNUmakefile new file mode 100644 index 0000000..278350b --- /dev/null +++ b/src/pmdas/mounts/GNUmakefile @@ -0,0 +1,64 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mounts +DOMAIN = MOUNTS +TARGETS = $(IAM)$(EXECSUFFIX) +CFILES = mounts.c +SCRIPTS = Install Remove +DFILES = README +LSRCFILES= $(SCRIPTS) pmns help root $(DFILES) mounts.conf + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMCHART = $(PCP_VAR_DIR)/config/pmchart + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so $(TARGETS) \ + help.pag help.dir +LLDLIBS = $(PCP_PMDALIB) + +default: build-me + +include $(BUILDRULES) + +# This PMDA is only valid on platforms with a mount table (e.g. /proc/mounts) +ifeq "$(findstring $(TARGET_OS),mingw darwin)" "" +build-me: $(TARGETS) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) pmns help root domain.h $(PMDADIR) + $(INSTALL) -m 644 mounts.conf $(PMDADIR)/mounts.conf +else +build-me: +install: +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +default_pcp: default + +install_pcp: install + +mounts.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/mounts/Install b/src/pmdas/mounts/Install new file mode 100755 index 0000000..e690a1f --- /dev/null +++ b/src/pmdas/mounts/Install @@ -0,0 +1,30 @@ +#! /bin/sh +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the mounts PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mounts +pmda_interface=2 +forced_restart=false +pmdaSetup +pmdaInstall + +exit 0 diff --git a/src/pmdas/mounts/README b/src/pmdas/mounts/README new file mode 100644 index 0000000..2bf3c6a --- /dev/null +++ b/src/pmdas/mounts/README @@ -0,0 +1,72 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# + +Mounts PMDA +============ + +This PMDA exports information about the mount status of file +systems specified in a config file. The default config file is +$PCP_PMDAS_DIR/mounts/mounts.conf, which should contain one line +for each file system you wish to monitor. + +This source code was contributed by Alan Bailey (abailey@ncsa.uiuc.edu) +to the PCP open source project. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT mounts + +Installation +============ + + + # cd $PCP_PMDAS_DIR/mounts + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use + ($PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + + + Alternatively, to install just the Performance Metrics Name Space + for the mounts metrics on the local system, but not the mounts PMDA + (presumably because the local system is running PCP 1.x and you + wish to connect to a remote system where PCP 2.0 and the mounts PMDA + is running), make sure the Performance Metrics Domain defined in + ./domain.h matches the domain chosen for the mounts PMDA on the + remote system (check the second field in the corresponding line of + the $PCP_PMCDCONF_PATH file on the remote system), then + + # ./Install -N + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/mounts + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/mounts.log) should be checked for any warnings or + errors. diff --git a/src/pmdas/mounts/Remove b/src/pmdas/mounts/Remove new file mode 100755 index 0000000..fac32b9 --- /dev/null +++ b/src/pmdas/mounts/Remove @@ -0,0 +1,41 @@ +#! /bin/sh +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the mounts PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=mounts + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/mounts/help b/src/pmdas/mounts/help new file mode 100644 index 0000000..d8cbbf4 --- /dev/null +++ b/src/pmdas/mounts/help @@ -0,0 +1,47 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# mounts PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ mounts.device Tracks the current mounts +Tracks the current mounts + +@ mounts.type Tracks the types of current mounts +Tracks the types of current mounts + +@ mounts.options Tracks the mount options of current mounts +Tracks the mount options of current mounts + +@ mounts.up Simply tells that the mount is up +Simply tells that the mount is up diff --git a/src/pmdas/mounts/mounts.c b/src/pmdas/mounts/mounts.c new file mode 100644 index 0000000..44ec6d7 --- /dev/null +++ b/src/pmdas/mounts/mounts.c @@ -0,0 +1,384 @@ +/* + * Mounts, info on current mounts + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2001,2003,2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) + * for the portions of the code supporting the initial agent functionality. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include +#include + +/* + * Mounts PMDA + * + * Metrics + * mounts.device + * The device which the mount is mounted on + * mounts.type + * The type of filesystem + * mounts.options + * The mounting options + * mounts.up + * always equals 1 + */ + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +#ifdef IS_SOLARIS +#define MOUNT_FILE "/etc/vfstab" +#else +#define MOUNT_FILE "/proc/mounts" +#endif + +static pmdaInstid *mounts; + +static pmdaIndom indomtab[] = { +#define MOUNTS_INDOM 0 + { MOUNTS_INDOM, 0, NULL } +}; + +static pmdaMetric metrictab[] = { +/* mounts.device */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_STRING, MOUNTS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* mounts.type */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_STRING, MOUNTS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* mounts.options */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_STRING, MOUNTS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* mounts.up */ + { NULL, + { PMDA_PMID(0,3), PM_TYPE_DOUBLE, MOUNTS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +typedef struct { + int up; + char device[100]; + char type[100]; + char options[100]; +} mountinfo; + +static mountinfo *mount_list; +static struct stat file_change; +static int isDSO = 1; +static char mypath[MAXPATHLEN]; +static char *username; + +static void mounts_clear_config_info(void); +static void mounts_grab_config_info(void); +static void mounts_config_file_check(void); +static void mounts_refresh_mounts(void); + +static void +mounts_config_file_check(void) +{ + struct stat statbuf; + static int last_error; + int sep = __pmPathSeparator(); + + snprintf(mypath, sizeof(mypath), "%s%c" "mounts" "%c" "mounts.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if (stat(mypath, &statbuf) == -1) { + if (oserror() != last_error) { + last_error = oserror(); + __pmNotifyErr(LOG_WARNING, "stat failed on %s: %s\n", + mypath, pmErrStr(last_error)); + } + } else { + last_error = 0; +#if defined(HAVE_ST_MTIME_WITH_E) + if (statbuf.st_mtime != file_change.st_mtime) +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + if (statbuf.st_mtimespec.tv_sec != file_change.st_mtimespec.tv_sec || + statbuf.st_mtimespec.tv_nsec != file_change.st_mtimespec.tv_nsec) +#else + if (statbuf.st_mtim.tv_sec != file_change.st_mtim.tv_sec || + statbuf.st_mtim.tv_nsec != file_change.st_mtim.tv_nsec) +#endif + { + mounts_clear_config_info(); + mounts_grab_config_info(); + file_change = statbuf; + } + } +} + +static void +mounts_clear_config_info(void) +{ + int i; + + /* Free the memory holding the mount name */ + for (i = 0; i < indomtab[MOUNTS_INDOM].it_numinst; i++) { + free(mounts[i].i_name); + mounts[i].i_name = NULL; + } + + /* Free the mounts structure */ + if (mounts) + free(mounts); + + /* Free the mount_list structure */ + if (mount_list) + free(mount_list); + + mount_list = NULL; + indomtab[MOUNTS_INDOM].it_set = mounts = NULL; + indomtab[MOUNTS_INDOM].it_numinst = 0; +} + +/* + * This routine opens the config file and stores the information in the + * mounts structure. The mounts structure must be reallocated as + * necessary, and also the num_procs structure needs to be reallocated + * as we define new mounts. When all of that is done, we fill in the + * values in the indomtab structure, those being the number of instances + * and the pointer to the mounts structure. + */ +static void +mounts_grab_config_info(void) +{ + FILE *fp; + char mount_name[MAXPATHLEN]; + char *q; + size_t size; + int mount_number = 0; + int sep = __pmPathSeparator(); + + snprintf(mypath, sizeof(mypath), "%s%c" "mounts" "%c" "mounts.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if ((fp = fopen(mypath, "r")) == NULL) { + __pmNotifyErr(LOG_ERR, "fopen on %s failed: %s\n", + mypath, pmErrStr(-oserror())); + if (mounts) { + free(mounts); + mounts = NULL; + mount_number = 0; + } + goto done; + } + + while (fgets(mount_name, sizeof(mount_name), fp) != NULL) { + if (mount_name[0] == '#') + continue; + /* Remove the newline */ + if ((q = strchr(mount_name, '\n')) != NULL) { + *q = '\0'; + } else { + /* This means the line was too long */ + __pmNotifyErr(LOG_WARNING, "line %d in the config file too long\n", + mount_number+1); + } + size = (mount_number + 1) * sizeof(pmdaInstid); + if ((mounts = realloc(mounts, size)) == NULL) + __pmNoMem("process", size, PM_FATAL_ERR); + mounts[mount_number].i_name = malloc(strlen(mount_name) + 1); + strcpy(mounts[mount_number].i_name, mount_name); + mounts[mount_number].i_inst = mount_number; + mount_number++; + } + fclose(fp); + +done: + if (mounts == NULL) + __pmNotifyErr(LOG_WARNING, "\"mounts\" instance domain is empty"); + indomtab[MOUNTS_INDOM].it_set = mounts; + indomtab[MOUNTS_INDOM].it_numinst = mount_number; + mount_list = realloc(mount_list, (mount_number)*sizeof(mountinfo)); +} + +static void +mounts_refresh_mounts(void) +{ + FILE *fd; + char device[100]; + char mount[100]; + char type[100]; + char options[100]; + char junk[10]; + int item; + int mount_name; + + /* Clear the variables */ + for(item = 0; item < indomtab[MOUNTS_INDOM].it_numinst; item++) { + strcpy(mount_list[item].device, "none"); + strcpy(mount_list[item].type, "none"); + strcpy(mount_list[item].options, "none"); + mount_list[item].up = 0; + } + + if ((fd = fopen(MOUNT_FILE, "r")) != NULL) { +#ifdef IS_SOLARIS + char device_to_fsck[100]; + char fsck_pass[100]; + char mount_at_boot[100]; + + while ((fscanf(fd, "%s %s %s %s %s %s %s", + device, device_to_fsck, mount, type, fsck_pass, + mount_at_boot, options)) == 7) +#else + while ((fscanf(fd, "%s %s %s %s", device, mount, type, options)) == 4) +#endif + { + if (fgets(junk, sizeof(junk), fd) == NULL) { + /* early EOF? will be caught in next iteration */ + ; + } + + for (mount_name = 0; + mount_name < indomtab[MOUNTS_INDOM].it_numinst; + mount_name++) { + if (strcmp(mount, (mounts[mount_name]).i_name) == 0) { + strcpy(mount_list[mount_name].device, device); + strcpy(mount_list[mount_name].type, type); + strcpy(mount_list[mount_name].options, options); + mount_list[mount_name].up = 1; + } + } + memset(device, 0, sizeof(device)); + memset(mount, 0, sizeof(mount)); + memset(type, 0, sizeof(type)); + memset(options, 0, sizeof(options)); + } + fclose(fd); + } +} + +/* + * This is the wrapper over the pmdaFetch routine, to handle the problem + * of varying instance domains. All this does is delete the previous + * mount list, and then get the current one, by calling + * mounts_refresh_mounts. + */ +static int +mounts_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + mounts_config_file_check(); + mounts_refresh_mounts(); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * callback provided to pmdaFetch + */ +static int +mounts_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (idp->cluster != 0) + return PM_ERR_PMID; + if (inst >= indomtab[MOUNTS_INDOM].it_numinst) + return PM_ERR_INST; + + if (idp->item == 0) + atom->cp = (mount_list[inst]).device; + else if (idp->item == 1) + atom->cp = (mount_list[inst]).type; + else if (idp->item == 2) + atom->cp = (mount_list[inst]).options; + else if (idp->item == 3) + atom->d = (mount_list[inst]).up; + else + return PM_ERR_PMID; + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +mounts_init(pmdaInterface *dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "mounts" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_2, "mounts DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.two.fetch = mounts_fetch; + pmdaSetFetchCallBack(dp, mounts_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); + + /* Let's grab the info right away just to make sure it's there. */ + mounts_grab_config_info(); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface desc; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "mounts" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, MOUNTS, + "mounts.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&desc); + mounts_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + exit(0); +} diff --git a/src/pmdas/mounts/mounts.conf b/src/pmdas/mounts/mounts.conf new file mode 100644 index 0000000..217607f --- /dev/null +++ b/src/pmdas/mounts/mounts.conf @@ -0,0 +1,10 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +/ +/boot +/afs +/not-here diff --git a/src/pmdas/mounts/pmns b/src/pmdas/mounts/pmns new file mode 100644 index 0000000..b328773 --- /dev/null +++ b/src/pmdas/mounts/pmns @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) + * for the portions of the code supporting the initial agent functionality. + * All rights reserved. + * Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Metrics for mounts PMDA + */ + +mounts { + device MOUNTS:0:0 + type MOUNTS:0:1 + options MOUNTS:0:2 + up MOUNTS:0:3 +} diff --git a/src/pmdas/mounts/root b/src/pmdas/mounts/root new file mode 100644 index 0000000..1dbb4a3 --- /dev/null +++ b/src/pmdas/mounts/root @@ -0,0 +1,15 @@ +/* + * fake "root" for validating the local PMNS subtree + * + * Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) + * for the portions of the code supporting the initial agent functionality. + * All rights reserved. + * Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include + +root { mounts } + +#include "pmns" + diff --git a/src/pmdas/mssql/GNUmakefile b/src/pmdas/mssql/GNUmakefile new file mode 100644 index 0000000..c76670e --- /dev/null +++ b/src/pmdas/mssql/GNUmakefile @@ -0,0 +1,53 @@ +#!gmake +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mssql +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/mssql/Install b/src/pmdas/mssql/Install new file mode 100644 index 0000000..bd222fa --- /dev/null +++ b/src/pmdas/mssql/Install @@ -0,0 +1,34 @@ +#! /bin/sh +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the MSSQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mssql +perl_opt=true +daemon_opt=false +forced_restart=false + +perl -e "use DBI" 2>/dev/null +if test $? -ne 0; then + echo "Perl database interface (DBI) is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/mssql/Remove b/src/pmdas/mssql/Remove new file mode 100644 index 0000000..1408c07 --- /dev/null +++ b/src/pmdas/mssql/Remove @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the MSSQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mssql + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/mssql/pmdamssql.pl b/src/pmdas/mssql/pmdamssql.pl new file mode 100644 index 0000000..e82fb62 --- /dev/null +++ b/src/pmdas/mssql/pmdamssql.pl @@ -0,0 +1,315 @@ +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use DBI; + +my $server = 'localhost'; +my $database = 'PCP'; +my $username = 'dbmonitor'; +my $password = 'dbmonitor'; + +# Configuration files for overriding the above settings +for my $file ( '/etc/pcpdbi.conf', # system defaults (lowest priority) + pmda_config('PCP_PMDAS_DIR') . '/mssql/mssql.conf', + './mssql.conf' ) { # current directory (high priority) + eval `cat $file` unless ! -f $file; +} + +use vars qw( $pmda $dbh ); +use vars qw( $sth_os_memory_clerks ); +use vars qw( $sth_virtual_file_stats @virtual_file_stats ); +use vars qw( $sth_total_running_user_processes @total_running_user_processes ); +use vars qw( $sth_os_memory_clerks @os_memory_clerks ); +use vars qw( $sth_os_workers_waiting_cpu @os_workers_waiting_cpu ); +my $database_indom = 0; +my @database_instances; + +sub mssql_connection_setup +{ + #$pmda->log("mssql_connection_setup\n"); + + if (!defined($dbh)) { + $dbh = DBI->connect("DBI:Sybase:server=$server", $username, $password); + if (defined($dbh)) { + $pmda->log("MSSQL connection established\n"); + $sth_virtual_file_stats = $dbh->prepare( + "select db_name(database_id), cast(num_of_reads as numeric), cast(num_of_bytes_read as numeric)," . + " cast(io_stall_read_ms as numeric), cast(num_of_writes as numeric)," . + " cast(num_of_bytes_written as numeric), cast(io_stall_write_ms as numeric)," . + " cast(size_on_disk_bytes as numeric) " . + "from sys.dm_io_virtual_file_stats(DB_ID(''),1)"); + $sth_os_memory_clerks = $dbh->prepare( + "SELECT SUM(multi_pages_kb + virtual_memory_committed_kb + shared_memory_committed_kb + awe_allocated_kb)" . + " from sys.dm_os_memory_clerks WHERE type IN " . + "('MEMORYCLERK_SQLBUFFERPOOL', 'MEMORYCLERK_SQLQUERYCOMPILE'," . + " 'MEMORYCLERK_SQLQUERYEXEC', 'MEMORYCLERK_SQLQUERYPLAN')" . + " group by type order by type"); + $sth_total_running_user_processes = $dbh->prepare( + "SELECT count(*) FROM sys.dm_exec_requests " . + "WHERE session_id >= 51 AND status = 'running'"); + $sth_os_workers_waiting_cpu = $dbh->prepare( + "SELECT ISNULL(COUNT(*),0) FROM sys.dm_os_workers AS workers " . + "INNER JOIN sys.dm_os_schedulers AS schedulers " . + "ON workers.scheduler_address = schedulers.scheduler_address " . + "WHERE workers.state = 'RUNNABLE' AND schedulers.scheduler_id < 255"); + } + } +} + +sub mssql_os_memory_clerks_refresh +{ + #$pmda->log("mssql_os_memory_clerks_refresh\n"); + + @os_memory_clerks = (); # clear any previous contents + if (defined($dbh)) { + $sth_os_memory_clerks->execute(); + my $result = $sth_os_memory_clerks->fetchall_arrayref(); + for my $i (0 .. $#{$result}) { + $os_memory_clerks[$i] = $result->[$i][0]; + } + } +} + +sub mssql_virtual_file_stats_refresh +{ + #$pmda->log("mssql_virtual_file_stats_refresh\n"); + + @virtual_file_stats = (); # clear any previous contents + @database_instances = (); + + if (defined($dbh)) { + $sth_virtual_file_stats->execute(); + my $result = $sth_virtual_file_stats->fetchall_arrayref(); + + for my $i (0 .. $#{$result}) { + $database_instances[($i*2)] = $i; + $database_instances[($i*2)+1] = "$result->[$i][0]"; + $virtual_file_stats[$i] = $result->[$i]; + } + + $pmda->replace_indom( $database_indom, \@database_instances ); + } +} + +sub mssql_total_running_user_processes +{ + #$pmda->log("mssql_total_running_user_processes\n"); + + @total_running_user_processes = (); # clear any previous contents + if (defined($dbh)) { + $sth_total_running_user_processes->execute(); + my $result = $sth_total_running_user_processes->fetchall_arrayref(); + @total_running_user_processes = ( $result->[0][0] ); + } +} + +sub mssql_os_workers_waiting_cpu_refresh +{ + #$pmda->log("mssql_os_workers_refresh\n"); + + @os_workers_waiting_cpu = (); # clear any previous contents + if (defined($dbh)) { + $sth_os_workers_waiting_cpu->execute(); + my $result = $sth_os_workers_waiting_cpu->fetchall_arrayref(); + @os_workers_waiting_cpu = ( $result->[0][0] ); + } +} + +sub mssql_refresh +{ + my ($cluster) = @_; + + #$pmda->log("mssql_refresh $cluster\n"); + + if ($cluster == 0) { mssql_virtual_file_stats_refresh; } + elsif ($cluster == 1) { mssql_os_memory_clerks_refresh; } + elsif ($cluster == 2) { mssql_total_running_user_processes; } + elsif ($cluster == 3) { mssql_os_workers_waiting_cpu_refresh; } +} + +sub mssql_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my ($value, @vfstats); + + #$pmda->log("mssql_fetch_callback $cluster:$item ($inst)\n"); + + if ($cluster == 0) { + if ($item > 6) { return (PM_ERR_PMID, 0); } + if ($inst < 0) { return (PM_ERR_INST, 0); } + if ($inst > @database_instances) { return (PM_ERR_INST, 0); } + $value = $virtual_file_stats[$inst]; + if (!defined($value)) { return (PM_ERR_INST, 0); } + @vfstats = @$value; + if (!defined($vfstats[$item+1])) { return (PM_ERR_AGAIN, 0); } + return ($vfstats[$item+1], 1); + } + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + if ($cluster == 1) { + if ($item > 3) { return (PM_ERR_PMID, 0); } + if (!defined($os_memory_clerks[$item])) { return (PM_ERR_AGAIN, 0); } + return ($os_memory_clerks[$item], 1); + } + if ($cluster == 2) { + if ($item > 0) { return (PM_ERR_PMID, 0); } + if (!defined($total_running_user_processes[$item])) { return (PM_ERR_AGAIN, 0); } + return ($total_running_user_processes[$item], 1); + } + if ($cluster == 3) { + if ($item > 0) { return (PM_ERR_PMID, 0); } + if (!defined($os_workers_waiting_cpu[$item])) { return (PM_ERR_AGAIN, 0); } + return ($os_workers_waiting_cpu[$item], 1); + } + return (PM_ERR_PMID, 0); +} + +$pmda = PCP::PMDA->new('mssql', 109); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U64, $database_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mssql.virtual_file.read', 'Number of bytes reads issued on data file', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U64, $database_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mssql.virtual_file.read_bytes', 'Total number of bytes read on the data file', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U64, $database_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'mssql.virtual_file.read_io_stall_time', 'Total time in ms that the users waited for reads issued on the file', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U64, $database_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mssql.virtual_file.write', 'Number of writes made on the data file', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U64, $database_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mssql.virtual_file.write_bytes', 'Total number of bytes written to the data file', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U64, $database_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'mssql.virtual_file.write_io_stall_time', 'Total time in ms that users waited for writes to be completed o the file', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U64, $database_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mssql.virtual_file.size', 'Number of bytes used on the disk from the data file', ''); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'mssql.os_memory_clerks.bufferpool', '', ''); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'mssql.os_memory_clerks.querycompile', '', ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'mssql.os_memory_clerks.queryexec', '', ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'mssql.os_memory_clerks.queryplan', '', ''); + +$pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mssql.running_user_process.total', 'Total number of running user process belonging to aconexsq', ''); + +$pmda->add_metric(pmda_pmid(3,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mssql.os_workers_waiting_cpu.count', 'Total number of queries waiting for cpu', ''); + +$pmda->add_indom($database_indom, \@database_instances, + 'Instance domain exporting each MSSQL database', ''); + +$pmda->set_fetch_callback(\&mssql_fetch_callback); +$pmda->set_fetch(\&mssql_connection_setup); +$pmda->set_refresh(\&mssql_refresh); +$pmda->run; + +=pod + +=head1 NAME + +pmdamssql - Microsoft SQL database PMDA + +=head1 DESCRIPTION + +B is a Performance Co-Pilot PMDA which extracts +live performance data from a running SQL Server database. +These metrics are typically sourced from Dynamic Management +Views (DMVs), augmenting the SQL server metrics exported by +the Windows PMDA. + +=head1 INSTALLATION + +B uses a configuration file from (in this order): + +=over + +=item * /etc/pcpdbi.conf + +=item * $PCP_PMDAS_DIR/mssql/mssql.conf + +=back + +This file can contain overridden values (Perl code) for the settings +listed at the start of pmdamssql.pl, namely: + +=over + +=item * database name (see DBI(3) for details) + +=item * database user name + +=item * database pass word + +=back + +Once this is setup, you can access the names and values for the +mysql performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/mssql + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/mssql + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item /etc/pcpdbi.conf + +configuration file for all PCP database monitors + +=item $PCP_PMDAS_DIR/mssql/mssql.conf + +configuration file for B + +=item $PCP_PMDAS_DIR/mssql/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/mssql/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/mssql.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), pmdadbping.pl(1) and DBI(3). diff --git a/src/pmdas/mysql/GNUmakefile b/src/pmdas/mysql/GNUmakefile new file mode 100644 index 0000000..b836e0c --- /dev/null +++ b/src/pmdas/mysql/GNUmakefile @@ -0,0 +1,52 @@ +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mysql +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl pmlogconf.summary migrate.conf README + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 README $(PMDADIR)/README + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary + $(INSTALL) -m 644 migrate.conf $(PCP_VAR_DIR)/config/pmlogrewrite/mysql_migrate.conf + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/mysql/Install b/src/pmdas/mysql/Install new file mode 100755 index 0000000..c98edce --- /dev/null +++ b/src/pmdas/mysql/Install @@ -0,0 +1,34 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the MySQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mysql +perl_opt=true +daemon_opt=false +forced_restart=false + +perl -e "use DBI" 2>/dev/null +if test $? -ne 0; then + echo "Perl database interface (DBI) is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/mysql/README b/src/pmdas/mysql/README new file mode 100644 index 0000000..971046c --- /dev/null +++ b/src/pmdas/mysql/README @@ -0,0 +1,79 @@ +Performance Co-Pilot PMDA for Monitoring MySQL Databases +======================================================== + +This PMDA exports activity and performance metrics from a MySQL +database server on the local system. + +The PMDA collects its data from the SQL commands: + show variables; + show global status; + show processlist; + show slave status; + +Metrics +======= + +Once the PMDA has been installed, the following command will list all of +the available metrics: + + + # $ pminfo -f mysql + +Database Setup +============== + +The PMDA needs access to the mysql database. If you use the PMDA +as shipped, this implies a MySQL user 'dbmonitor' with password +'dbmonitor' has been created and has access to the mysql database. + +Specifically, this means the following has been done: + + + # mysql -uroot -p... + mysql> create user 'dbmonitor'@'localhost' identified by 'dbmonitor'; + mysql> grant select on mysql.* to 'dbmonitor'@'localhost'; + mysql> grant replication client on *.* to 'dbmonitor'@'localhost'; + +If this username and password combination does not suit, choose +some other, but you'll have to change these intializations in +pmdamysql.pl: + + my $username = 'dbmonitor'; + my $password = 'dbmonitor'; + +Installation +============ + + + # cd $PCP_PMDAS_DIR/mysql + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDA's currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number (This should only be an issue on installations with + third party PMDA's installed as the domain number given has been + reserved for the mysql PMDA with base PCP installations). + + + Then simply use + + # ./Install + + and choose both the “collector” and “monitor” installation + configuration options. + +De-Installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/mysql + #./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/PMCD/mysql.log) should be checked for any warnings or + errors. + + + In an event where no values are being returned for most of the + metrics check ensure that the username and password in pmdamysql.pl + match the local MySQL setup. diff --git a/src/pmdas/mysql/Remove b/src/pmdas/mysql/Remove new file mode 100755 index 0000000..77999bb --- /dev/null +++ b/src/pmdas/mysql/Remove @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the MySQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mysql + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/mysql/migrate.conf b/src/pmdas/mysql/migrate.conf new file mode 100644 index 0000000..de35280 --- /dev/null +++ b/src/pmdas/mysql/migrate.conf @@ -0,0 +1,23 @@ +# Copyright 2013 Ken McDonell. All Rights Reserved +# +# pmlogrewrite configuration for migrating archives containing MySQL metrics +# across various changes in the metadata supplied by the PMDA +# + +# was instantaneous, now counter as per commit 1ec908d on 24 Jun 2013 +# +metric mysql.status.connections { + sem -> counter + units -> 0,0,1,0,0,ONE +} + +# uptime metrics were counters, now instantaneous as per commit 8de75d1 +# on Aug 28 2014 +metric mysql.status.uptime { + sem -> instant +} +metric mysql.status.uptime_since_flush_status { + sem -> instant +} + + diff --git a/src/pmdas/mysql/pmdamysql.pl b/src/pmdas/mysql/pmdamysql.pl new file mode 100644 index 0000000..56f7093 --- /dev/null +++ b/src/pmdas/mysql/pmdamysql.pl @@ -0,0 +1,1911 @@ +# +# Copyright (c) 2012-2013 Chandana De Silva. +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use DBI; + +my $database = 'DBI:mysql:mysql'; +my $username = 'dbmonitor'; +my $password = 'dbmonitor'; + +# Configuration files for overriding the above settings +for my $file ( '/etc/pcpdbi.conf', # system defaults (lowest priority) + pmda_config('PCP_PMDAS_DIR') . '/mysql/mysql.conf', + './mysql.conf' ) { # current directory (high priority) + eval `cat $file` unless ! -f $file; +} + +use vars qw( $pmda %status %variables @processes %slave_status ); +use vars qw( $dbh $sth_variables $sth_status $sth_processes $sth_slave_status ); +my $process_indom = 0; +my @process_instances; + +# translate yes/no true/false on/off to 1/0 +sub mysql_txt2num { + my ($value) = lc($_[0]); + + if (!defined($value)) { + return (PM_ERR_AGAIN, 0); + } + elsif ($value eq "yes" || $value eq "true" || $value eq "on") { + return 1; + } + elsif ($value eq "no" || $value eq "false" || $value eq "off") { + return 0; + } + else { + return -1; + } +} + +sub mysql_connection_setup +{ + # $pmda->log("mysql_connection_setup\n"); + + if (!defined($dbh)) { + $dbh = DBI->connect($database, $username, $password); + if (defined($dbh)) { + # set the db handle to undef in case of any failure + # this will force a database reconnect + $dbh->{HandleError} = sub { $dbh = undef; }; + $pmda->log("MySQL connection established\n"); + $sth_variables = $dbh->prepare('show variables'); + $sth_status = $dbh->prepare('show global status'); + $sth_processes = $dbh->prepare('show processlist'); + $sth_slave_status = $dbh->prepare('show slave status'); + } + } +} + +sub mysql_variables_refresh +{ + # $pmda->log("mysql_variables_refresh\n"); + + %variables = (); # clear any previous contents + if (defined($dbh)) { + $sth_variables->execute(); + my $result = $sth_variables->fetchall_arrayref(); + for my $i (0 .. $#{$result}) { + $variables{$result->[$i][0]} = $result->[$i][1]; + } + } +} + +sub mysql_status_refresh +{ + # $pmda->log("mysql_status_refresh\n"); + + %status = (); # clear any previous contents + if (defined($dbh)) { + $sth_status->execute(); + my $result = $sth_status->fetchall_arrayref(); + my $txtnum; + my $txtnumvar; + for my $i (0 .. $#{$result}) { + my $key = lcfirst $result->[$i][0]; + $status{$key} = $result->[$i][1]; + # if this status value has a yes/no type value, get it translated + $txtnum = mysql_txt2num($result->[$i][1]); + if ($txtnum ge 0) { + $txtnumvar=$key . "_num"; + $status{$txtnumvar} = $txtnum; + } + } + } +} + +sub mysql_process_refresh +{ + # $pmda->log("mysql_process_refresh\n"); + + @processes = (); # clear any previous contents + @process_instances = (); # refresh indom too + + if (defined($dbh)) { + $sth_processes->execute(); + my $result = $sth_processes->fetchall_arrayref(); + for my $i (0 .. $#{$result}) { + $process_instances[($i*2)] = $i; + $process_instances[($i*2)+1] = "$result->[$i][0]"; + $processes[$i] = $result->[$i]; + } + } + + $pmda->replace_indom($process_indom, \@process_instances); +} + +sub mysql_slave_status_refresh +{ + # $pmda->log("mysql_slave_status_refresh\n"); + + %slave_status = (); # clear any previous contents + if (defined($dbh)) { + $sth_slave_status->execute(); + my $result = $sth_slave_status->fetchrow_hashref(); + my $txtnum; + my $txtnumvar; + while ( my ($key, $value) = each(%$result) ) { + $slave_status{lc $key} = $value; + # if this status value has a yes/no type value, get it translated + $txtnum = mysql_txt2num($value); + if ($txtnum ge 0) { + $txtnumvar=lc($key) . "_num"; + $slave_status{$txtnumvar} = $txtnum; + } + } + } +} + +sub mysql_refresh +{ + my ($cluster) = @_; + + # $pmda->log("mysql_refresh $cluster\n"); + if ($cluster == 0) { mysql_status_refresh; } + elsif ($cluster == 1) { mysql_variables_refresh; } + elsif ($cluster == 2) { mysql_process_refresh; } + elsif ($cluster == 3) { mysql_slave_status_refresh; } +} + +sub mysql_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ($mysql_name, $value, @procs); + + # $pmda->log("mysql_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + if (!defined($metric_name)) { return (PM_ERR_PMID, 0); } + $mysql_name = $metric_name; + + if ($cluster == 2) { + if ($inst < 0) { return (PM_ERR_INST, 0); } + if ($inst > @process_instances) { return (PM_ERR_INST, 0); } + $value = $processes[$inst]; + if (!defined($value)) { return (PM_ERR_INST, 0); } + @procs = @$value; + if (!defined($procs[$item]) && $item == 6) { return ("?", 1); } + if (!defined($procs[$item])) { return (PM_ERR_APPVERSION, 0); } + return ($procs[$item], 1); + } + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + if ($cluster == 0) { + $mysql_name =~ s/^mysql\.status\.//; + $value = $status{$mysql_name}; + if (!defined($value)) { return (PM_ERR_APPVERSION, 0); } + return ($value, 1); + } + elsif ($cluster == 1) { + $mysql_name =~ s/^mysql\.variables\.//; + $value = $variables{$mysql_name}; + if (!defined($value)) { return (PM_ERR_APPVERSION, 0); } + return ($value, 1); + } + elsif ($cluster == 3) { + $mysql_name =~ s/^mysql\.slave_status\.//; + $value = $slave_status{$mysql_name}; + if (!defined($value)) { return (PM_ERR_APPVERSION, 0); } + return ($value, 1); + } + return (PM_ERR_PMID, 0); +} + +$pmda = PCP::PMDA->new('mysql', 66); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.aborted_clients', '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.aborted_connects', '', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.binlog_cache_disk_use', '', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.binlog_cache_use', '', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.bytes_received', '', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.bytes_sent', '', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_admin_commands', '', ''); +$pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_alter_db', '', ''); +$pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_alter_table', '', ''); +$pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_analyze', '', ''); +$pmda->add_metric(pmda_pmid(0,10), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_backup_table', '', ''); +$pmda->add_metric(pmda_pmid(0,11), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_begin', '', ''); +$pmda->add_metric(pmda_pmid(0,12), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_call_procedure', '', ''); +$pmda->add_metric(pmda_pmid(0,13), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_change_db', '', ''); +$pmda->add_metric(pmda_pmid(0,14), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_change_master', '', ''); +$pmda->add_metric(pmda_pmid(0,15), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_check', '', ''); +$pmda->add_metric(pmda_pmid(0,16), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_checksum', '', ''); +$pmda->add_metric(pmda_pmid(0,17), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_commit', '', ''); +$pmda->add_metric(pmda_pmid(0,18), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_create_db', '', ''); +$pmda->add_metric(pmda_pmid(0,19), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_create_function', '', ''); +$pmda->add_metric(pmda_pmid(0,20), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_create_index', '', ''); +$pmda->add_metric(pmda_pmid(0,21), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_create_table', '', ''); +$pmda->add_metric(pmda_pmid(0,22), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_create_user', '', ''); +$pmda->add_metric(pmda_pmid(0,23), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_dealloc_sql', '', ''); +$pmda->add_metric(pmda_pmid(0,24), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_delete', '', ''); +$pmda->add_metric(pmda_pmid(0,25), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_delete_multi', '', ''); +$pmda->add_metric(pmda_pmid(0,26), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_do', '', ''); +$pmda->add_metric(pmda_pmid(0,27), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_drop_db', '', ''); +$pmda->add_metric(pmda_pmid(0,28), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_drop_function', '', ''); +$pmda->add_metric(pmda_pmid(0,29), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_drop_index', '', ''); +$pmda->add_metric(pmda_pmid(0,30), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_drop_table', '', ''); +$pmda->add_metric(pmda_pmid(0,31), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_drop_user', '', ''); +$pmda->add_metric(pmda_pmid(0,32), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_execute_sql', '', ''); +$pmda->add_metric(pmda_pmid(0,33), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_flush', '', ''); +$pmda->add_metric(pmda_pmid(0,34), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_grant', '', ''); +$pmda->add_metric(pmda_pmid(0,35), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_ha_close', '', ''); +$pmda->add_metric(pmda_pmid(0,36), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_ha_open', '', ''); +$pmda->add_metric(pmda_pmid(0,37), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_ha_read', '', ''); +$pmda->add_metric(pmda_pmid(0,38), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_help', '', ''); +$pmda->add_metric(pmda_pmid(0,39), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_insert', '', ''); +$pmda->add_metric(pmda_pmid(0,40), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_insert_select', '', ''); +$pmda->add_metric(pmda_pmid(0,41), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_kill', '', ''); +$pmda->add_metric(pmda_pmid(0,42), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_load', '', ''); +$pmda->add_metric(pmda_pmid(0,43), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_load_master_data', '', ''); +$pmda->add_metric(pmda_pmid(0,44), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_load_master_table', '', ''); +$pmda->add_metric(pmda_pmid(0,45), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_lock_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,46), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_optimize', '', ''); +$pmda->add_metric(pmda_pmid(0,47), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_preload_keys', '', ''); +$pmda->add_metric(pmda_pmid(0,48), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_prepare_sql', '', ''); +$pmda->add_metric(pmda_pmid(0,49), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_purge', '', ''); +$pmda->add_metric(pmda_pmid(0,50), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_purge_before_date', '', ''); +$pmda->add_metric(pmda_pmid(0,51), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_rename_table', '', ''); +$pmda->add_metric(pmda_pmid(0,52), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_repair', '', ''); +$pmda->add_metric(pmda_pmid(0,53), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_replace', '', ''); +$pmda->add_metric(pmda_pmid(0,54), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_replace_select', '', ''); +$pmda->add_metric(pmda_pmid(0,55), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_reset', '', ''); +$pmda->add_metric(pmda_pmid(0,56), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_restore_table', '', ''); +$pmda->add_metric(pmda_pmid(0,57), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_revoke', '', ''); +$pmda->add_metric(pmda_pmid(0,58), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_revoke_all', '', ''); +$pmda->add_metric(pmda_pmid(0,59), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_rollback', '', ''); +$pmda->add_metric(pmda_pmid(0,60), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_savepoint', '', ''); +$pmda->add_metric(pmda_pmid(0,61), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_select', '', ''); +$pmda->add_metric(pmda_pmid(0,62), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_set_option', '', ''); +$pmda->add_metric(pmda_pmid(0,63), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_binlog_events', '', ''); +$pmda->add_metric(pmda_pmid(0,64), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_binlogs', '', ''); +$pmda->add_metric(pmda_pmid(0,65), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_charsets', '', ''); +$pmda->add_metric(pmda_pmid(0,66), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_collations', '', ''); +$pmda->add_metric(pmda_pmid(0,67), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_column_types', '', ''); +$pmda->add_metric(pmda_pmid(0,68), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_create_db', '', ''); +$pmda->add_metric(pmda_pmid(0,69), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_create_table', '', ''); +$pmda->add_metric(pmda_pmid(0,70), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_databases', '', ''); +$pmda->add_metric(pmda_pmid(0,71), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_errors', '', ''); +$pmda->add_metric(pmda_pmid(0,72), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_fields', '', ''); +$pmda->add_metric(pmda_pmid(0,73), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_grants', '', ''); +$pmda->add_metric(pmda_pmid(0,74), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_innodb_status', '', ''); +$pmda->add_metric(pmda_pmid(0,75), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_keys', '', ''); +$pmda->add_metric(pmda_pmid(0,76), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_logs', '', ''); +$pmda->add_metric(pmda_pmid(0,77), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_master_status', '', ''); +$pmda->add_metric(pmda_pmid(0,78), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_ndb_status', '', ''); +$pmda->add_metric(pmda_pmid(0,79), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_new_master', '', ''); +$pmda->add_metric(pmda_pmid(0,80), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_open_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,81), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_privileges', '', ''); +$pmda->add_metric(pmda_pmid(0,82), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_processlist', '', ''); +$pmda->add_metric(pmda_pmid(0,83), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_slave_hosts', '', ''); +$pmda->add_metric(pmda_pmid(0,84), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_slave_status', '', ''); +$pmda->add_metric(pmda_pmid(0,85), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_status', '', ''); +$pmda->add_metric(pmda_pmid(0,86), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_storage_engines', '', ''); +$pmda->add_metric(pmda_pmid(0,87), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,88), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_triggers', '', ''); +$pmda->add_metric(pmda_pmid(0,89), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_variables', '', ''); +$pmda->add_metric(pmda_pmid(0,90), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_show_warnings', '', ''); +$pmda->add_metric(pmda_pmid(0,91), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_slave_start', '', ''); +$pmda->add_metric(pmda_pmid(0,92), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_slave_stop', '', ''); +$pmda->add_metric(pmda_pmid(0,93), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_stmt_close', '', ''); +$pmda->add_metric(pmda_pmid(0,94), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_stmt_execute', '', ''); +$pmda->add_metric(pmda_pmid(0,95), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_stmt_fetch', '', ''); +$pmda->add_metric(pmda_pmid(0,96), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_stmt_prepare', '', ''); +$pmda->add_metric(pmda_pmid(0,97), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_stmt_reset', '', ''); +$pmda->add_metric(pmda_pmid(0,98), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_stmt_send_long_data', '', ''); +$pmda->add_metric(pmda_pmid(0,99), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_truncate', '', ''); +$pmda->add_metric(pmda_pmid(0,100), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_unlock_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,101), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_update', '', ''); +$pmda->add_metric(pmda_pmid(0,102), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_update_multi', '', ''); +$pmda->add_metric(pmda_pmid(0,103), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_xa_commit', '', ''); +$pmda->add_metric(pmda_pmid(0,104), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_xa_end', '', ''); +$pmda->add_metric(pmda_pmid(0,105), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_xa_prepare', '', ''); +$pmda->add_metric(pmda_pmid(0,106), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_xa_recover', '', ''); +$pmda->add_metric(pmda_pmid(0,107), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_xa_rollback', '', ''); +$pmda->add_metric(pmda_pmid(0,108), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.com_xa_start', '', ''); +$pmda->add_metric(pmda_pmid(0,109), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.compression', '', ''); +$pmda->add_metric(pmda_pmid(0,110), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.connections', '', ''); +$pmda->add_metric(pmda_pmid(0,111), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.created_tmp_disk_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,112), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.created_tmp_files', '', ''); +$pmda->add_metric(pmda_pmid(0,113), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.created_tmp_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,114), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.delayed_errors', '', ''); +$pmda->add_metric(pmda_pmid(0,115), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.delayed_insert_threads', '', ''); +$pmda->add_metric(pmda_pmid(0,116), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.delayed_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,117), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.flush_commands', '', ''); +$pmda->add_metric(pmda_pmid(0,118), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_commit', '', ''); +$pmda->add_metric(pmda_pmid(0,119), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_delete', '', ''); +$pmda->add_metric(pmda_pmid(0,120), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_discover', '', ''); +$pmda->add_metric(pmda_pmid(0,121), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_prepare', '', ''); +$pmda->add_metric(pmda_pmid(0,122), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_read_first', '', ''); +$pmda->add_metric(pmda_pmid(0,123), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_read_key', '', ''); +$pmda->add_metric(pmda_pmid(0,124), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_read_next', '', ''); +$pmda->add_metric(pmda_pmid(0,125), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_read_prev', '', ''); +$pmda->add_metric(pmda_pmid(0,126), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_read_rnd', '', ''); +$pmda->add_metric(pmda_pmid(0,127), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_read_rnd_next', '', ''); +$pmda->add_metric(pmda_pmid(0,128), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_rollback', '', ''); +$pmda->add_metric(pmda_pmid(0,129), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_savepoint', '', ''); +$pmda->add_metric(pmda_pmid(0,130), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_savepoint_rollback', '', ''); +$pmda->add_metric(pmda_pmid(0,131), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_update', '', ''); +$pmda->add_metric(pmda_pmid(0,132), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.handler_write', '', ''); +$pmda->add_metric(pmda_pmid(0,133), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_data', '', ''); +$pmda->add_metric(pmda_pmid(0,134), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_dirty', '', ''); +$pmda->add_metric(pmda_pmid(0,135), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_flushed', '', ''); +$pmda->add_metric(pmda_pmid(0,136), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_free', '', ''); +$pmda->add_metric(pmda_pmid(0,137), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_latched', '', ''); +$pmda->add_metric(pmda_pmid(0,138), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_misc', '', ''); +$pmda->add_metric(pmda_pmid(0,139), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_pages_total', '', ''); +$pmda->add_metric(pmda_pmid(0,140), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_read_ahead_rnd', '', ''); +$pmda->add_metric(pmda_pmid(0,141), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_read_ahead_seq', '', ''); +$pmda->add_metric(pmda_pmid(0,142), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_read_requests', '', ''); +$pmda->add_metric(pmda_pmid(0,143), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_reads', '', ''); +$pmda->add_metric(pmda_pmid(0,144), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_wait_free', '', ''); +$pmda->add_metric(pmda_pmid(0,145), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_buffer_pool_write_requests', '', ''); +$pmda->add_metric(pmda_pmid(0,146), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_data_fsyncs', '', ''); +$pmda->add_metric(pmda_pmid(0,147), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_data_pending_fsyncs', '', ''); +$pmda->add_metric(pmda_pmid(0,148), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_data_pending_reads', '', ''); +$pmda->add_metric(pmda_pmid(0,149), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_data_pending_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,150), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.innodb_data_read', '', ''); +$pmda->add_metric(pmda_pmid(0,151), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_data_reads', '', ''); +$pmda->add_metric(pmda_pmid(0,152), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_data_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,153), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.innodb_data_written', '', ''); +$pmda->add_metric(pmda_pmid(0,154), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_dblwr_pages_written', '', ''); +$pmda->add_metric(pmda_pmid(0,155), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_dblwr_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,156), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_log_waits', '', ''); +$pmda->add_metric(pmda_pmid(0,157), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_log_write_requests', '', ''); +$pmda->add_metric(pmda_pmid(0,158), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_log_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,159), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_os_log_fsyncs', '', ''); +$pmda->add_metric(pmda_pmid(0,160), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_os_log_pending_fsyncs', '', ''); +$pmda->add_metric(pmda_pmid(0,161), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_os_log_pending_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,162), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.innodb_os_log_written', '', ''); +$pmda->add_metric(pmda_pmid(0,163), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.innodb_page_size', '', ''); +$pmda->add_metric(pmda_pmid(0,164), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_pages_created', '', ''); +$pmda->add_metric(pmda_pmid(0,165), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_pages_read', '', ''); +$pmda->add_metric(pmda_pmid(0,166), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_pages_written', '', ''); +$pmda->add_metric(pmda_pmid(0,167), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_row_lock_current_waits', '', ''); +$pmda->add_metric(pmda_pmid(0,168), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'mysql.status.innodb_row_lock_time', '', ''); +$pmda->add_metric(pmda_pmid(0,169), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'mysql.status.innodb_row_lock_time_avg', '', ''); +$pmda->add_metric(pmda_pmid(0,170), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'mysql.status.innodb_row_lock_time_max', '', ''); +$pmda->add_metric(pmda_pmid(0,171), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_row_lock_waits', '', ''); +$pmda->add_metric(pmda_pmid(0,172), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_rows_deleted', '', ''); +$pmda->add_metric(pmda_pmid(0,173), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_rows_inserted', '', ''); +$pmda->add_metric(pmda_pmid(0,174), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_rows_read', '', ''); +$pmda->add_metric(pmda_pmid(0,175), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.innodb_rows_updated', '', ''); +$pmda->add_metric(pmda_pmid(0,176), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_blocks_not_flushed', '', ''); +$pmda->add_metric(pmda_pmid(0,177), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_blocks_unused', '', ''); +$pmda->add_metric(pmda_pmid(0,178), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_blocks_used', '', ''); +$pmda->add_metric(pmda_pmid(0,179), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_read_requests', '', ''); +$pmda->add_metric(pmda_pmid(0,180), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_reads', '', ''); +$pmda->add_metric(pmda_pmid(0,181), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_write_requests', '', ''); +$pmda->add_metric(pmda_pmid(0,182), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.key_writes', '', ''); +$pmda->add_metric(pmda_pmid(0,183), PM_TYPE_DOUBLE, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.last_query_cost', '', ''); +$pmda->add_metric(pmda_pmid(0,184), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.max_used_connections', '', ''); +$pmda->add_metric(pmda_pmid(0,185), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ndb_cluster_node_id', '', ''); +$pmda->add_metric(pmda_pmid(0,186), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ndb_config_from_host', '', ''); +$pmda->add_metric(pmda_pmid(0,187), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ndb_config_from_port', '', ''); +$pmda->add_metric(pmda_pmid(0,188), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ndb_number_of_data_nodes', '', ''); +$pmda->add_metric(pmda_pmid(0,189), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.not_flushed_delayed_rows', '', ''); +$pmda->add_metric(pmda_pmid(0,190), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.open_files', '', ''); +$pmda->add_metric(pmda_pmid(0,191), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.open_streams', '', ''); +$pmda->add_metric(pmda_pmid(0,192), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.open_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,193), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.opened_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,194), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.prepared_stmt_count', '', ''); +$pmda->add_metric(pmda_pmid(0,195), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.qcache_free_blocks', '', ''); +$pmda->add_metric(pmda_pmid(0,196), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.status.qcache_free_memory', '', ''); +$pmda->add_metric(pmda_pmid(0,197), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.qcache_hits', '', ''); +$pmda->add_metric(pmda_pmid(0,198), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.qcache_inserts', '', ''); +$pmda->add_metric(pmda_pmid(0,199), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.qcache_lowmem_prunes', '', ''); +$pmda->add_metric(pmda_pmid(0,200), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.qcache_not_cached', '', ''); +$pmda->add_metric(pmda_pmid(0,201), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.qcache_queries_in_cache', '', ''); +$pmda->add_metric(pmda_pmid(0,202), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.qcache_total_blocks', '', ''); +$pmda->add_metric(pmda_pmid(0,203), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.questions', '', ''); +$pmda->add_metric(pmda_pmid(0,204), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.rpl_status', '', ''); +$pmda->add_metric(pmda_pmid(0,205), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.select_full_join', '', ''); +$pmda->add_metric(pmda_pmid(0,206), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.select_full_range_join', '', ''); +$pmda->add_metric(pmda_pmid(0,207), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.select_range', '', ''); +$pmda->add_metric(pmda_pmid(0,208), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.select_range_check', '', ''); +$pmda->add_metric(pmda_pmid(0,209), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.select_scan', '', ''); +$pmda->add_metric(pmda_pmid(0,210), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.slave_open_temp_tables', '', ''); +$pmda->add_metric(pmda_pmid(0,211), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.slave_retried_transactions', '', ''); +$pmda->add_metric(pmda_pmid(0,212), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.slave_running', '', ''); +$pmda->add_metric(pmda_pmid(0,213), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.slow_launch_threads', '', ''); +$pmda->add_metric(pmda_pmid(0,214), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.slow_queries', '', ''); +$pmda->add_metric(pmda_pmid(0,215), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.sort_merge_passes', '', ''); +$pmda->add_metric(pmda_pmid(0,216), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.sort_range', '', ''); +$pmda->add_metric(pmda_pmid(0,217), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.sort_rows', '', ''); +$pmda->add_metric(pmda_pmid(0,218), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.sort_scan', '', ''); +$pmda->add_metric(pmda_pmid(0,219), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_accept_renegotiates', '', ''); +$pmda->add_metric(pmda_pmid(0,220), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_accepts', '', ''); +$pmda->add_metric(pmda_pmid(0,221), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_callback_cache_hits', '', ''); +$pmda->add_metric(pmda_pmid(0,222), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_cipher', '', ''); +$pmda->add_metric(pmda_pmid(0,223), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_cipher_list', '', ''); +$pmda->add_metric(pmda_pmid(0,224), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_client_connects', '', ''); +$pmda->add_metric(pmda_pmid(0,225), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_connect_renegotiates', '', ''); +$pmda->add_metric(pmda_pmid(0,226), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_ctx_verify_depth', '', ''); +$pmda->add_metric(pmda_pmid(0,227), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_ctx_verify_mode', '', ''); +$pmda->add_metric(pmda_pmid(0,228), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.status.ssl_default_timeout', '', ''); +$pmda->add_metric(pmda_pmid(0,229), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_finished_accepts', '', ''); +$pmda->add_metric(pmda_pmid(0,230), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_finished_connects', '', ''); +$pmda->add_metric(pmda_pmid(0,231), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_session_cache_hits', '', ''); +$pmda->add_metric(pmda_pmid(0,232), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_session_cache_misses', '', ''); +$pmda->add_metric(pmda_pmid(0,233), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_session_cache_mode', '', ''); +$pmda->add_metric(pmda_pmid(0,234), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_session_cache_overflows', '', ''); +$pmda->add_metric(pmda_pmid(0,235), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_session_cache_size', '', ''); +$pmda->add_metric(pmda_pmid(0,236), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_session_cache_timeouts', '', ''); +$pmda->add_metric(pmda_pmid(0,237), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.ssl_sessions_reused', '', ''); +$pmda->add_metric(pmda_pmid(0,238), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_used_session_cache_entries', '', ''); +$pmda->add_metric(pmda_pmid(0,239), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_verify_depth', '', ''); +$pmda->add_metric(pmda_pmid(0,240), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_verify_mode', '', ''); +$pmda->add_metric(pmda_pmid(0,241), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.ssl_version', '', ''); +$pmda->add_metric(pmda_pmid(0,242), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.table_locks_immediate', '', ''); +$pmda->add_metric(pmda_pmid(0,243), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.table_locks_waited', '', ''); +$pmda->add_metric(pmda_pmid(0,244), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.tc_log_max_pages_used', '', ''); +$pmda->add_metric(pmda_pmid(0,245), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.tc_log_page_size', '', ''); +$pmda->add_metric(pmda_pmid(0,246), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.status.tc_log_page_waits', '', ''); +$pmda->add_metric(pmda_pmid(0,247), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.threads_cached', '', ''); +$pmda->add_metric(pmda_pmid(0,248), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.threads_connected', '', ''); +$pmda->add_metric(pmda_pmid(0,249), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.threads_created', '', ''); +$pmda->add_metric(pmda_pmid(0,250), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.threads_running', '', ''); +$pmda->add_metric(pmda_pmid(0,251), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.status.uptime', '', ''); +$pmda->add_metric(pmda_pmid(0,252), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.status.uptime_since_flush_status', '', ''); +$pmda->add_metric(pmda_pmid(0,253), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.slave_running_num', '', ''); +$pmda->add_metric(pmda_pmid(0,254), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.status.compression_num', '', ''); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.auto_increment_increment', '', ''); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.auto_increment_offset', '', ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.automatic_sp_privileges', '', ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.back_log', '', ''); +$pmda->add_metric(pmda_pmid(1,4), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.basedir', '', ''); +$pmda->add_metric(pmda_pmid(1,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.binlog_cache_size', '', ''); +$pmda->add_metric(pmda_pmid(1,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.bulk_insert_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,6), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_client', '', ''); +$pmda->add_metric(pmda_pmid(1,7), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_connection', '', ''); +$pmda->add_metric(pmda_pmid(1,8), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_database', '', ''); +$pmda->add_metric(pmda_pmid(1,9), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_filesystem', '', ''); +$pmda->add_metric(pmda_pmid(1,10), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_results', '', ''); +$pmda->add_metric(pmda_pmid(1,11), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_server', '', ''); +$pmda->add_metric(pmda_pmid(1,12), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_set_system', '', ''); +$pmda->add_metric(pmda_pmid(1,13), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.character_sets_dir', '', ''); +$pmda->add_metric(pmda_pmid(1,14), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.collation_connection', '', ''); +$pmda->add_metric(pmda_pmid(1,15), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.collation_database', '', ''); +$pmda->add_metric(pmda_pmid(1,16), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.collation_server', '', ''); +$pmda->add_metric(pmda_pmid(1,17), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.completion_type', '', ''); +$pmda->add_metric(pmda_pmid(1,18), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.concurrent_insert', '', ''); +$pmda->add_metric(pmda_pmid(1,19), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.connect_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,20), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.datadir', '', ''); +$pmda->add_metric(pmda_pmid(1,21), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.date_format', '', ''); +$pmda->add_metric(pmda_pmid(1,22), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.datetime_format', '', ''); +$pmda->add_metric(pmda_pmid(1,23), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.default_week_format', '', ''); +$pmda->add_metric(pmda_pmid(1,24), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.delay_key_write', '', ''); +$pmda->add_metric(pmda_pmid(1,25), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.delayed_insert_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,26), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.delayed_insert_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,27), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.delayed_queue_size', '', ''); +$pmda->add_metric(pmda_pmid(1,28), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.div_precision_increment', '', ''); +$pmda->add_metric(pmda_pmid(1,29), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.keep_files_on_create', '', ''); +$pmda->add_metric(pmda_pmid(1,30), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.engine_condition_pushdown', '', ''); +$pmda->add_metric(pmda_pmid(1,31), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.expire_logs_days', '', ''); +$pmda->add_metric(pmda_pmid(1,32), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.flush', '', ''); +$pmda->add_metric(pmda_pmid(1,33), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.flush_time', '', ''); +$pmda->add_metric(pmda_pmid(1,34), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ft_boolean_syntax', '', ''); +$pmda->add_metric(pmda_pmid(1,35), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ft_max_word_len', '', ''); +$pmda->add_metric(pmda_pmid(1,35), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ft_min_word_len', '', ''); +$pmda->add_metric(pmda_pmid(1,35), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ft_query_expansion_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,36), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ft_stopword_file', '', ''); +$pmda->add_metric(pmda_pmid(1,37), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.group_concat_max_len', '', ''); +$pmda->add_metric(pmda_pmid(1,38), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_archive', '', ''); +$pmda->add_metric(pmda_pmid(1,39), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_bdb', '', ''); +$pmda->add_metric(pmda_pmid(1,40), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_blackhole_engine', '', ''); +$pmda->add_metric(pmda_pmid(1,41), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_compress', '', ''); +$pmda->add_metric(pmda_pmid(1,42), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_crypt', '', ''); +$pmda->add_metric(pmda_pmid(1,43), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_csv', '', ''); +$pmda->add_metric(pmda_pmid(1,44), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_dynamic_loading', '', ''); +$pmda->add_metric(pmda_pmid(1,45), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_example_engine', '', ''); +$pmda->add_metric(pmda_pmid(1,46), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_federated_engine', '', ''); +$pmda->add_metric(pmda_pmid(1,47), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_geometry', '', ''); +$pmda->add_metric(pmda_pmid(1,48), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_innodb', '', ''); +$pmda->add_metric(pmda_pmid(1,49), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_isam', '', ''); +$pmda->add_metric(pmda_pmid(1,50), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_merge_engine', '', ''); +$pmda->add_metric(pmda_pmid(1,51), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_ndbcluster', '', ''); +$pmda->add_metric(pmda_pmid(1,52), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_openssl', '', ''); +$pmda->add_metric(pmda_pmid(1,53), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_ssl', '', ''); +$pmda->add_metric(pmda_pmid(1,54), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_query_cache', '', ''); +$pmda->add_metric(pmda_pmid(1,55), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_raid', '', ''); +$pmda->add_metric(pmda_pmid(1,56), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_rtree_keys', '', ''); +$pmda->add_metric(pmda_pmid(1,57), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.have_symlink', '', ''); +$pmda->add_metric(pmda_pmid(1,58), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.hostname', '', ''); +$pmda->add_metric(pmda_pmid(1,59), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.init_connect', '', ''); +$pmda->add_metric(pmda_pmid(1,60), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.init_file', '', ''); +$pmda->add_metric(pmda_pmid(1,61), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.init_slave', '', ''); +$pmda->add_metric(pmda_pmid(1,63), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_additional_mem_pool_size', '', ''); +$pmda->add_metric(pmda_pmid(1,64), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_autoextend_increment', '', ''); +$pmda->add_metric(pmda_pmid(1,65), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), + 'mysql.variables.innodb_buffer_pool_awe_mem_mb', '', ''); +$pmda->add_metric(pmda_pmid(1,66), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_buffer_pool_size', '', ''); +$pmda->add_metric(pmda_pmid(1,67), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_checksums', '', ''); +$pmda->add_metric(pmda_pmid(1,68), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_commit_concurrency', '', ''); +$pmda->add_metric(pmda_pmid(1,68), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_concurrency_tickets', '', ''); +$pmda->add_metric(pmda_pmid(1,69), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_data_file_path', '', ''); +$pmda->add_metric(pmda_pmid(1,70), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_data_home_dir', '', ''); +$pmda->add_metric(pmda_pmid(1,71), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_doublewrite', '', ''); +$pmda->add_metric(pmda_pmid(1,72), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_fast_shutdown', '', ''); +$pmda->add_metric(pmda_pmid(1,73), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_file_io_threads', '', ''); +$pmda->add_metric(pmda_pmid(1,74), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_file_per_table', '', ''); +$pmda->add_metric(pmda_pmid(1,75), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_flush_log_at_trx_commit', '', ''); +$pmda->add_metric(pmda_pmid(1,76), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_flush_method', '', ''); +$pmda->add_metric(pmda_pmid(1,77), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_force_recovery', '', ''); +$pmda->add_metric(pmda_pmid(1,78), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_lock_wait_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,79), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_locks_unsafe_for_binlog', '', ''); +$pmda->add_metric(pmda_pmid(1,80), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_log_arch_dir', '', ''); +$pmda->add_metric(pmda_pmid(1,81), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_log_archive', '', ''); +$pmda->add_metric(pmda_pmid(1,82), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_log_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,83), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_log_file_size', '', ''); +$pmda->add_metric(pmda_pmid(1,84), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_log_files_in_group', '', ''); +$pmda->add_metric(pmda_pmid(1,85), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_log_group_home_dir', '', ''); +$pmda->add_metric(pmda_pmid(1,86), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_max_dirty_pages_pct', '', ''); +$pmda->add_metric(pmda_pmid(1,87), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_max_purge_lag', '', ''); +$pmda->add_metric(pmda_pmid(1,88), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_mirrored_log_groups', '', ''); +$pmda->add_metric(pmda_pmid(1,89), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_open_files', '', ''); +$pmda->add_metric(pmda_pmid(1,90), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_rollback_on_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,91), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_support_xa', '', ''); +$pmda->add_metric(pmda_pmid(1,92), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_sync_spin_loops', '', ''); +$pmda->add_metric(pmda_pmid(1,93), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_table_locks', '', ''); +$pmda->add_metric(pmda_pmid(1,94), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_thread_concurrency', '', ''); +$pmda->add_metric(pmda_pmid(1,95), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.innodb_thread_sleep_delay', '', ''); +$pmda->add_metric(pmda_pmid(1,96), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.interactive_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,97), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.join_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,98), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.key_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,99), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.key_cache_age_threshold', '', ''); +$pmda->add_metric(pmda_pmid(1,100), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.key_cache_block_size', '', ''); +$pmda->add_metric(pmda_pmid(1,101), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.key_cache_division_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,102), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.language', '', ''); +$pmda->add_metric(pmda_pmid(1,103), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.large_files_support', '', ''); +$pmda->add_metric(pmda_pmid(1,104), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.large_page_size', '', ''); +$pmda->add_metric(pmda_pmid(1,105), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.large_pages', '', ''); +$pmda->add_metric(pmda_pmid(1,106), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.lc_time_names', '', ''); +$pmda->add_metric(pmda_pmid(1,107), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.license', '', ''); +$pmda->add_metric(pmda_pmid(1,108), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.local_infile', '', ''); +$pmda->add_metric(pmda_pmid(1,109), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.locked_in_memory', '', ''); +$pmda->add_metric(pmda_pmid(1,110), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log', '', ''); +$pmda->add_metric(pmda_pmid(1,111), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_bin', '', ''); +$pmda->add_metric(pmda_pmid(1,112), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_bin_trust_function_creators', '', ''); +$pmda->add_metric(pmda_pmid(1,113), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_error', '', ''); +$pmda->add_metric(pmda_pmid(1,114), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_queries_not_using_indexes', '', ''); +$pmda->add_metric(pmda_pmid(1,115), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_slave_updates', '', ''); +$pmda->add_metric(pmda_pmid(1,116), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_slow_queries', '', ''); +$pmda->add_metric(pmda_pmid(1,117), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.log_warnings', '', ''); +$pmda->add_metric(pmda_pmid(1,118), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.long_query_time', '', ''); +$pmda->add_metric(pmda_pmid(1,119), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.low_priority_updates', '', ''); +$pmda->add_metric(pmda_pmid(1,120), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.lower_case_file_system', '', ''); +$pmda->add_metric(pmda_pmid(1,121), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.lower_case_table_names', '', ''); +$pmda->add_metric(pmda_pmid(1,122), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.max_allowed_packet', '', ''); +$pmda->add_metric(pmda_pmid(1,124), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.max_binlog_cache_size', '', ''); +$pmda->add_metric(pmda_pmid(1,125), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.max_binlog_size', '', ''); +$pmda->add_metric(pmda_pmid(1,126), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_connect_errors', '', ''); +$pmda->add_metric(pmda_pmid(1,127), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_connections', '', ''); +$pmda->add_metric(pmda_pmid(1,128), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_delayed_threads', '', ''); +$pmda->add_metric(pmda_pmid(1,129), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_error_count', '', ''); +$pmda->add_metric(pmda_pmid(1,130), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_heap_table_size', '', ''); +$pmda->add_metric(pmda_pmid(1,131), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_insert_delayed_threads', '', ''); +$pmda->add_metric(pmda_pmid(1,132), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_join_size', '', ''); +$pmda->add_metric(pmda_pmid(1,133), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_length_for_sort_data', '', ''); +$pmda->add_metric(pmda_pmid(1,134), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_prepared_stmt_count', '', ''); +$pmda->add_metric(pmda_pmid(1,135), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_relay_log_size', '', ''); +$pmda->add_metric(pmda_pmid(1,136), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_seeks_for_key', '', ''); +$pmda->add_metric(pmda_pmid(1,137), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_sort_length', '', ''); +$pmda->add_metric(pmda_pmid(1,138), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_sp_recursion_depth', '', ''); +$pmda->add_metric(pmda_pmid(1,139), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_tmp_tables', '', ''); +$pmda->add_metric(pmda_pmid(1,140), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_user_connections', '', ''); +$pmda->add_metric(pmda_pmid(1,141), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.max_write_lock_count', '', ''); +$pmda->add_metric(pmda_pmid(1,142), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.multi_range_count', '', ''); +$pmda->add_metric(pmda_pmid(1,143), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.myisam_data_pointer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,144), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.myisam_max_sort_file_size', '', ''); +$pmda->add_metric(pmda_pmid(1,145), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.myisam_recover_options', '', ''); +$pmda->add_metric(pmda_pmid(1,146), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.myisam_repair_threads', '', ''); +$pmda->add_metric(pmda_pmid(1,147), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.myisam_sort_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,148), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.myisam_stats_method', '', ''); +$pmda->add_metric(pmda_pmid(1,149), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.ndb_autoincrement_prefetch_sz', '', ''); +$pmda->add_metric(pmda_pmid(1,150), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ndb_force_send', '', ''); +$pmda->add_metric(pmda_pmid(1,151), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ndb_use_exact_count', '', ''); +$pmda->add_metric(pmda_pmid(1,152), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ndb_use_transactions', '', ''); +$pmda->add_metric(pmda_pmid(1,153), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.ndb_cache_check_time', '', ''); +$pmda->add_metric(pmda_pmid(1,154), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ndb_connectstring', '', ''); +$pmda->add_metric(pmda_pmid(1,155), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.net_buffer_length', '', ''); +$pmda->add_metric(pmda_pmid(1,156), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.net_read_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,157), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.net_retry_count', '', ''); +$pmda->add_metric(pmda_pmid(1,158), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.net_write_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,159), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.new', '', ''); +$pmda->add_metric(pmda_pmid(1,159), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.old_passwords', '', ''); +$pmda->add_metric(pmda_pmid(1,160), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.open_files_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,161), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.optimizer_prune_level', '', ''); +$pmda->add_metric(pmda_pmid(1,162), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.optimizer_search_depth', '', ''); +$pmda->add_metric(pmda_pmid(1,163), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.pid_file', '', ''); +$pmda->add_metric(pmda_pmid(1,164), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.port', '', ''); +$pmda->add_metric(pmda_pmid(1,165), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.preload_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,166), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.profiling', '', ''); +$pmda->add_metric(pmda_pmid(1,167), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.profiling_history_size', '', ''); +$pmda->add_metric(pmda_pmid(1,168), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.protocol_version', '', ''); +$pmda->add_metric(pmda_pmid(1,169), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.query_alloc_block_size', '', ''); +$pmda->add_metric(pmda_pmid(1,170), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.query_cache_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,171), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.query_cache_min_res_unit', '', ''); +$pmda->add_metric(pmda_pmid(1,172), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.query_cache_size', '', ''); +$pmda->add_metric(pmda_pmid(1,173), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.query_cache_type', '', ''); +$pmda->add_metric(pmda_pmid(1,174), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.query_cache_wlock_invalidate', '', ''); +$pmda->add_metric(pmda_pmid(1,175), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.query_prealloc_size', '', ''); +$pmda->add_metric(pmda_pmid(1,176), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.range_alloc_block_size', '', ''); +$pmda->add_metric(pmda_pmid(1,177), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.read_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,178), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.read_only', '', ''); +$pmda->add_metric(pmda_pmid(1,179), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.read_rnd_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,180), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.relay_log_purge', '', ''); +$pmda->add_metric(pmda_pmid(1,181), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.relay_log_space_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,182), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.rpl_recovery_rank', '', ''); +$pmda->add_metric(pmda_pmid(1,183), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.secure_auth', '', ''); +$pmda->add_metric(pmda_pmid(1,184), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.secure_file_priv', '', ''); +$pmda->add_metric(pmda_pmid(1,185), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.server_id', '', ''); +$pmda->add_metric(pmda_pmid(1,186), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.skip_external_locking', '', ''); +$pmda->add_metric(pmda_pmid(1,187), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.skip_networking', '', ''); +$pmda->add_metric(pmda_pmid(1,188), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.skip_show_database', '', ''); +$pmda->add_metric(pmda_pmid(1,189), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.slave_compressed_protocol', '', ''); +$pmda->add_metric(pmda_pmid(1,190), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.slave_load_tmpdir', '', ''); +$pmda->add_metric(pmda_pmid(1,191), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.slave_skip_errors', '', ''); +$pmda->add_metric(pmda_pmid(1,192), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.slave_net_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,193), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.slave_transaction_retries', '', ''); +$pmda->add_metric(pmda_pmid(1,194), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.slow_launch_time', '', ''); +$pmda->add_metric(pmda_pmid(1,195), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.socket', '', ''); +$pmda->add_metric(pmda_pmid(1,196), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.sort_buffer_size', '', ''); +$pmda->add_metric(pmda_pmid(1,197), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.sql_big_selects', '', ''); +$pmda->add_metric(pmda_pmid(1,198), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.sql_mode', '', ''); +$pmda->add_metric(pmda_pmid(1,199), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.sql_notes', '', ''); +$pmda->add_metric(pmda_pmid(1,200), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.sql_warnings', '', ''); +$pmda->add_metric(pmda_pmid(1,201), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ssl_ca', '', ''); +$pmda->add_metric(pmda_pmid(1,202), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ssl_capath', '', ''); +$pmda->add_metric(pmda_pmid(1,203), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ssl_cert', '', ''); +$pmda->add_metric(pmda_pmid(1,204), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ssl_cipher', '', ''); +$pmda->add_metric(pmda_pmid(1,205), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.ssl_key', '', ''); +$pmda->add_metric(pmda_pmid(1,206), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.storage_engine', '', ''); +$pmda->add_metric(pmda_pmid(1,207), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'mysql.variables.sync_binlog', '', ''); +$pmda->add_metric(pmda_pmid(1,208), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.sync_frm', '', ''); +$pmda->add_metric(pmda_pmid(1,209), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.system_time_zone', '', ''); +$pmda->add_metric(pmda_pmid(1,210), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.table_cache', '', ''); +$pmda->add_metric(pmda_pmid(1,211), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.table_lock_wait_timeout', '', ''); +$pmda->add_metric(pmda_pmid(1,212), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.table_type', '', ''); +$pmda->add_metric(pmda_pmid(1,211), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.thread_cache_size', '', ''); +$pmda->add_metric(pmda_pmid(1,212), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.thread_stack', '', ''); +$pmda->add_metric(pmda_pmid(1,213), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.time_format', '', ''); +$pmda->add_metric(pmda_pmid(1,214), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.time_zone', '', ''); +$pmda->add_metric(pmda_pmid(1,215), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.timed_mutexes', '', ''); +$pmda->add_metric(pmda_pmid(1,216), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.variables.tmp_table_size', '', ''); +$pmda->add_metric(pmda_pmid(1,217), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.tmpdir', '', ''); +$pmda->add_metric(pmda_pmid(1,218), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.transaction_alloc_block_size', '', ''); +$pmda->add_metric(pmda_pmid(1,219), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.variables.transaction_prealloc_size', '', ''); +$pmda->add_metric(pmda_pmid(1,220), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.tx_isolation', '', ''); +$pmda->add_metric(pmda_pmid(1,221), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.updatable_views_with_limit', '', ''); +$pmda->add_metric(pmda_pmid(1,222), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.version', '', ''); +$pmda->add_metric(pmda_pmid(1,223), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.version_comment', '', ''); +$pmda->add_metric(pmda_pmid(1,224), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.version_compile_machine', '', ''); +$pmda->add_metric(pmda_pmid(1,225), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.variables.version_compile_os', '', ''); +$pmda->add_metric(pmda_pmid(1,226), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.variables.wait_timeout', '', ''); + +$pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.id', '', ''); +$pmda->add_metric(pmda_pmid(2,1), PM_TYPE_STRING, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.user', '', ''); +$pmda->add_metric(pmda_pmid(2,2), PM_TYPE_STRING, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.host', '', ''); +$pmda->add_metric(pmda_pmid(2,3), PM_TYPE_STRING, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.db', '', ''); +$pmda->add_metric(pmda_pmid(2,4), PM_TYPE_STRING, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.command', '', ''); +$pmda->add_metric(pmda_pmid(2,5), PM_TYPE_U32, $process_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.processlist.time', '', ''); +$pmda->add_metric(pmda_pmid(2,6), PM_TYPE_STRING, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.state', '', ''); +$pmda->add_metric(pmda_pmid(2,7), PM_TYPE_STRING, $process_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.processlist.info', '', ''); + +$pmda->add_metric(pmda_pmid(3,0), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.slave_io_state', '', ''); +$pmda->add_metric(pmda_pmid(3,1), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.slave_io_running', '', ''); +$pmda->add_metric(pmda_pmid(3,2), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.slave_sql_running', '', ''); +$pmda->add_metric(pmda_pmid(3,3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.slave_status.seconds_behind_master', '', ''); +$pmda->add_metric(pmda_pmid(3,4), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_log_file', '', ''); +$pmda->add_metric(pmda_pmid(3,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.slave_status.read_master_log_pos', '', ''); +$pmda->add_metric(pmda_pmid(3,6), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.relay_master_log_file', '', ''); +$pmda->add_metric(pmda_pmid(3,7), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.slave_status.exec_master_log_pos', '', ''); +$pmda->add_metric(pmda_pmid(3,8), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.relay_log_file', '', ''); +$pmda->add_metric(pmda_pmid(3,9), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.slave_status.relay_log_pos', '', ''); +$pmda->add_metric(pmda_pmid(3,10), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.until_log_file', '', ''); +$pmda->add_metric(pmda_pmid(3,11), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.slave_status.until_log_pos', '', ''); +$pmda->add_metric(pmda_pmid(3,12), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_cipher', '', ''); +$pmda->add_metric(pmda_pmid(3,13), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_ca_file', '', ''); +$pmda->add_metric(pmda_pmid(3,14), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'mysql.slave_status.skip_counter', '', ''); +$pmda->add_metric(pmda_pmid(3,15), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'mysql.slave_status.relay_log_space', '', ''); +$pmda->add_metric(pmda_pmid(3,16), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.until_condition', '', ''); +$pmda->add_metric(pmda_pmid(3,17), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'mysql.slave_status.connect_retry', '', ''); +$pmda->add_metric(pmda_pmid(3,18), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_host', '', ''); +$pmda->add_metric(pmda_pmid(3,19), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.last_errno', '', ''); +$pmda->add_metric(pmda_pmid(3,20), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_cert', '', ''); +$pmda->add_metric(pmda_pmid(3,21), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.replicate_do_db', '', ''); +$pmda->add_metric(pmda_pmid(3,22), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.replicate_ignore_db', '', ''); +$pmda->add_metric(pmda_pmid(3,23), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_user', '', ''); +$pmda->add_metric(pmda_pmid(3,24), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.replicate_do_table', '', ''); +$pmda->add_metric(pmda_pmid(3,25), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.replicate_wild_do_table', '', ''); +$pmda->add_metric(pmda_pmid(3,26), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.replicate_wild_ignore_table', '', ''); +$pmda->add_metric(pmda_pmid(3,27), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.replicate_ignore_table', '', ''); +$pmda->add_metric(pmda_pmid(3,28), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_allowed', '', ''); +$pmda->add_metric(pmda_pmid(3,29), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_ca_path', '', ''); +$pmda->add_metric(pmda_pmid(3,30), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_port', '', ''); +$pmda->add_metric(pmda_pmid(3,31), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_key', '', ''); +$pmda->add_metric(pmda_pmid(3,32), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.slave_io_running_num', '', ''); +$pmda->add_metric(pmda_pmid(3,33), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.slave_sql_running_num', '', ''); +$pmda->add_metric(pmda_pmid(3,34), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'mysql.slave_status.master_ssl_allowed_num', '',''); + +$pmda->add_indom($process_indom, \@process_instances, + 'Instance domain exporting each MySQL process', ''); + +$pmda->set_fetch_callback(\&mysql_fetch_callback); +$pmda->set_fetch(\&mysql_connection_setup); +$pmda->set_refresh(\&mysql_refresh); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdamysql - MySQL database PMDA + +=head1 DESCRIPTION + +B is a Performance Co-Pilot PMDA which extracts +live performance data from a running MySQL database. + +=head1 INSTALLATION + +B uses a configuration file from (in this order): + +=over + +=item * /etc/pcpdbi.conf + +=item * $PCP_PMDAS_DIR/mysql/mysql.conf + +=back + +This file can contain overridden values (Perl code) for the settings +listed at the start of pmdamysql.pl, namely: + +=over + +=item * database name (see DBI(3) for details) + +=item * database user name + +=item * database pass word + +=back + +Once this is setup, you can access the names and values for the +mysql performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/mysql + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/mysql + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 Binary Status values in text + +Some of the status values are in the form of YES/NO or ON/OFF. + +Since these cannot be intepreted by tools like PMIE, +they have been duplicated with a _num extension +and the values of 1 (YES/ON) or 0 (NO/OFF). + +=head2 Eg: + +=over + +=item * mysql.slave_status.slave_io_running + +=item * mysql.slave_status.slave_io_running_num + +=back + +=head1 FILES + +=over + +=item /etc/pcpdbi.conf + +configuration file for all PCP database monitors + +=item $PCP_PMDAS_DIR/mysql/mysql.conf + +configuration file for B + +=item $PCP_PMDAS_DIR/mysql/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/mysql/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/mysql.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), pmdadbping.pl(1) and DBI(3). +# vi: sw=4 ts=4 et: diff --git a/src/pmdas/mysql/pmlogconf.summary b/src/pmdas/mysql/pmlogconf.summary new file mode 100644 index 0000000..d10aaed --- /dev/null +++ b/src/pmdas/mysql/pmlogconf.summary @@ -0,0 +1,4 @@ +#pmlogconf-setup 2.0 +ident mysql summary information +probe mysql.status exists ? include : exclude + mysql.status diff --git a/src/pmdas/named/GNUmakefile b/src/pmdas/named/GNUmakefile new file mode 100644 index 0000000..26591c1 --- /dev/null +++ b/src/pmdas/named/GNUmakefile @@ -0,0 +1,48 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = named +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/named/Install b/src/pmdas/named/Install new file mode 100755 index 0000000..2f83fe9 --- /dev/null +++ b/src/pmdas/named/Install @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the named PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=named +perl_opt=true +daemon_opt=false +forced_restart=true + +d="/var/named/data" +f="named_stats.txt" +if test ! -f "$d/$f" -a ! -f "/var/named/chroot/$d/$f" +then + echo "named does not appear to be exporting statistics (no $d/$f found)" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/named/Remove b/src/pmdas/named/Remove new file mode 100755 index 0000000..d50ad7b --- /dev/null +++ b/src/pmdas/named/Remove @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the named PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=named + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/named/pmdanamed.pl b/src/pmdas/named/pmdanamed.pl new file mode 100644 index 0000000..a0f03ba --- /dev/null +++ b/src/pmdas/named/pmdanamed.pl @@ -0,0 +1,190 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +my $pmda = PCP::PMDA->new('named', 100); +my @paths = ( '/var/named/chroot/var/named/data', '/var/named/data' ); +my $filename = 'named_stats.txt'; +my $statscmd = 'rdnc stats'; # writes to $paths/$filename +my ( $statsdir, $statsfile ); +my $interval = 10; # time (in seconds) between runs of $statscmd +my %values; # hash with all values mapped to metric names + +sub named_update +{ + #$pmda->log('Updating values in named statistics file'); + system 'rndc', 'stats'; +} + +# Parser formats (PMDA internal numbering scheme): +# 0: not yet known what ondisk format is +# 1: bind 8,9.[0-4] (<=rhel5.5) +# 2: bind 9.5+ (>=rhel5.6) +my $version = 0; + +sub named_parser +{ + ( undef, $_ ) = @_; + + #$pmda->log("named_parser got line: $_"); + + if (m|^\+\+\+ Statistics Dump \+\+\+ \(\d+\)$|) { + $version = 1; # all observed formats have this + } elsif (m|^\+\+ Incoming Requests \+\+$|) { + $version = 2; # but, new format has this also. + } + + if ($version == 2) { + if (m|^\s+(\d+) responses sent$|) { + $values{'named.nameserver.responses.sent'} = $1; + } elsif (m|^\s+(\d+) IPv4 requests received$|) { + $values{'named.nameserver.requests.IPv4'} = $1; + } elsif (m|^\s+(\d+) queries resulted in successful answer$|) { + $values{'named.nameserver.queries.successful'} = $1; + } elsif (m|^\s+(\d+) queries resulted in authoritative answer$|) { + $values{'named.nameserver.queries.authoritative'} = $1; + } elsif (m|^\s+(\d+) queries resulted in non authoritative answer$|) { + $values{'named.nameserver.queries.non_authoritative'} = $1; + } elsif (m|^\s+(\d+) queries resulted in nxrrset$|) { + $values{'named.nameserver.queries.nxrrset'} = $1; + } elsif (m|^\s+(\d+) queries resulted in NXDOMAIN$|) { + $values{'named.nameserver.queries.nxdomain'} = $1; + } elsif (m|^\s+(\d+) queries caused recursion$|) { + $values{'named.nameserver.queries.recursion'} = $1; + } + } + elsif ($version == 1) { + if (m|^success (\d+)$|) { $values{'named.success'} = $1; } + elsif (m|^referral (\d+)$|) { $values{'named.referral'} = $1; } + elsif (m|^nxrrset (\d+)$|) { $values{'named.nxrrset'} = $1; } + elsif (m|^nxdomain (\d+)$|) { $values{'named.nxdomain'} = $1; } + elsif (m|^recursion (\d+)$|) { $values{'named.recursion'} = $1; } + elsif (m|^failure (\d+)$|) { $values{'named.failure'} = $1; } + } +} + +sub named_metrics +{ + my $id = 0; + + open STATS, $statsfile || die "Cannot open statistics file: $statsfile\n"; + while () { named_parser(undef, $_); } + close STATS; + + $pmda->add_metric(pmda_pmid(0,$id++), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'named.interval', + 'Time interval between invocations of rndc stats command', ''); + foreach my $key (sort keys %values) { + $pmda->add_metric(pmda_pmid(0,$id++), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + $key, '', ''); + #$pmda->log("named_metrics adding $key metric"); + } +} + +sub named_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + if ($cluster != 0) { return (PM_ERR_PMID, 0); } + if ($item == 0) { return ($interval, 1); } + + my $metric_name = pmda_pmid_name($cluster, $item); + my $value = $values{$metric_name}; + #$pmda->log("named_fetch_callback for $metric_name: $cluster.$item"); + + return ($value, 1) if (defined($value)); + return (PM_ERR_PMID, 0); +} + +# PMDA starts here +foreach $statsdir ( @paths ) { + $statsfile = $statsdir . '/' . $filename; + last if ( -f $statsfile ); +} +die "Cannot find a valid named statistics file\n" unless -f $statsfile; +named_update(); # push some values into the statistics file + +$pmda->set_fetch_callback(\&named_fetch_callback); +$pmda->add_tail($statsfile, \&named_parser, 0); +$pmda->add_timer($interval, \&named_update, 0); +$pmda->set_user('named'); + +named_metrics(); # fetch/parse the stats file, create metrics +$pmda->run; + +=pod + +=head1 NAME + +pmdanamed - BIND (named) PMDA + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +exports metric values from the BIND DNS server. +Further details on BIND can be found at http://isc.org/. + +=head1 INSTALLATION + +If you want access to the names and values for the named performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/named + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/named + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item /var/named/data/named_stats.txt + +statistics file showing values exported from named + +=item /var/named/chroot/var/named/data/named_stats.txt + +chroot variant of statistics file showing values exported from named + +=item $PCP_PMDAS_DIR/named/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/named/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/named.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), named.conf(5), named(8). diff --git a/src/pmdas/netbsd/GNUmakefile b/src/pmdas/netbsd/GNUmakefile new file mode 100644 index 0000000..c904009 --- /dev/null +++ b/src/pmdas/netbsd/GNUmakefile @@ -0,0 +1,68 @@ +# +# Copyright (c) 2000,2003,2004,2008 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2007-2010 Aconex. All Rights Reserved. +# Copyright (c) 2012 Ken McDonell. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = netbsd +DOMAIN = NETBSD +CMDTARGET = pmdanetbsd +LIBTARGET = pmda_netbsd.so +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +CONF_LINE = "netbsd 116 dso netbsd_init $(PMDADIR)/$(LIBTARGET)" + +CFILES = netbsd.c disk.c netif.c + +HFILES = netbsd.h + +LSRCFILES = help root_netbsd +LDIRT = help.dir help.pag help.sed help.tmp domain.h $(IAM).log + +LLDLIBS = $(PCP_PMDALIB) -lkvm + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "netbsd" +build-me: domain.h $(LIBTARGET) $(CMDTARGET) help.dir help.pag + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h help help.dir help.pag $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root_netbsd $(PCP_VAR_DIR)/pmns/root_netbsd +else +build-me: +install: +endif + +help.dir help.pag : domain.h help + $(SED) help.sed -n -e '/#define/s/#define[ ]*\([A-Z][A-Z]*\)[ ]*\([0-9][0-9]*\)/s@\1@\2@/p' + $(SED) -f help.sed help.tmp + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_netbsd -v 2 -o help +struct devinfo devinfo = { 0 }; +struct statinfo statinfo; +#endif + +void +refresh_disk_metrics(void) +{ +#if 0 + static int init_done = 0; + int i; + int sts; + + if (!init_done) { + sts = devstat_checkversion(NULL); + if (sts != 0) { + fprintf(stderr, "refresh_disk_metrics: devstat_checkversion: failed! %s\n", devstat_errbuf); + exit(1); + } + statinfo.dinfo = &devinfo; + init_done = 1; + } + + sts = devstat_getdevs(NULL, &statinfo); + if (sts < 0) { + fprintf(stderr, "refresh_disk_metrics: devstat_getdevs: %s\n", strerror(errno)); + exit(1); + } + else if (sts == 1) { + /* + * First call, else devstat[] list has changed + */ + struct devstat *dsp; + char iname[DEVSTAT_NAME_LEN+6]; + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_INACTIVE); + for (i = 0; i < devinfo.numdevs; i++) { + dsp = &devinfo.devices[i]; + /* + * Skip entries that are not interesting ... only include + * "da" (direct access) disks at this stage + */ + if (strcmp(dsp->device_name, "da") != 0) + continue; + snprintf(iname, sizeof(iname), "%s%d", dsp->device_name, dsp->unit_number); + sts = pmdaCacheLookupName(indomtab[DISK_INDOM].it_indom, iname, NULL, NULL); + if (sts == PMDA_CACHE_ACTIVE) { + int j; + fprintf(stderr, "refresh_disk_metrics: Warning: duplicate name (%s) in disk indom\n", iname); + for (j = 0; j < devinfo.numdevs; j++) { + dsp = &devinfo.devices[j]; + fprintf(stderr, " devinfo[%d]: %s%d\n", j, dsp->device_name, dsp->unit_number); + } + continue; + } + else { + /* new entry or reactivate an existing one */ + pmdaCacheStore(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_ADD, iname, (void *)dsp); + } + } + } +#endif + +} + +int +do_disk_metrics(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ +#if 0 + struct devstat *dsp; + int sts; + + if (inst != PM_IN_NULL) { + /* + * per-disk metrics + */ + sts = pmdaCacheLookup(indomtab[DISK_INDOM].it_indom, inst, NULL, (void **)&dsp); + if (sts == PMDA_CACHE_ACTIVE) { + sts = 1; + /* cluster and domain already checked, just need item ... */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 0: /* disk.dev.read */ + atom->ull = dsp->operations[DEVSTAT_READ]; + break; + + case 1: /* disk.dev.write */ + atom->ull = dsp->operations[DEVSTAT_WRITE]; + break; + + case 2: /* disk.dev.total */ + atom->ull = dsp->operations[DEVSTAT_READ] + dsp->operations[DEVSTAT_WRITE]; + break; + + case 3: /* disk.dev.read_bytes */ + atom->ull = dsp->bytes[DEVSTAT_READ]; + break; + + case 4: /* disk.dev.write_bytes */ + atom->ull = dsp->bytes[DEVSTAT_WRITE]; + break; + + case 5: /* disk.dev.total_bytes */ + atom->ull = dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]; + break; + + case 12: /* disk.dev.blkread */ + atom->ull = dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_READ] / dsp->block_size; + break; + + case 13: /* disk.dev.blkwrite */ + atom->ull = dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_WRITE] / dsp->block_size; + break; + + case 14: /* disk.dev.blktotal */ + atom->ull = dsp->block_size == 0 ? 0 : (dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]) / dsp->block_size; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else + sts = 0; + } + else { + /* + * all-disk summary metrics + */ + int i; + atom->ull = 0; + sts = 1; + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_WALK_REWIND); + while (sts == 1 && (i = pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_WALK_NEXT)) >= 0) { + int lsts; + lsts = pmdaCacheLookup(indomtab[DISK_INDOM].it_indom, i, NULL, (void **)&dsp); + if (lsts == PMDA_CACHE_ACTIVE) { + /* cluster and domain already checked, just need item ... */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 6: /* disk.all.read */ + atom->ull += dsp->operations[DEVSTAT_READ]; + break; + + case 7: /* disk.all.write */ + atom->ull += dsp->operations[DEVSTAT_WRITE]; + break; + + case 8: /* disk.all.total */ + atom->ull += dsp->operations[DEVSTAT_READ] + dsp->operations[DEVSTAT_WRITE]; + break; + + case 9: /* disk.all.read_bytes */ + atom->ull += dsp->bytes[DEVSTAT_READ]; + break; + + case 10: /* disk.all.write_bytes */ + atom->ull += dsp->bytes[DEVSTAT_WRITE]; + break; + + case 11: /* disk.all.total_bytes */ + atom->ull += dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]; + break; + + case 15: /* disk.all.blkread */ + atom->ull += dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_READ] / dsp->block_size; + break; + + case 16: /* disk.all.blkwrite */ + atom->ull += dsp->block_size == 0 ? 0 : dsp->bytes[DEVSTAT_WRITE] / dsp->block_size; + break; + + case 17: /* disk.all.blktotal */ + atom->ull += dsp->block_size == 0 ? 0 : (dsp->bytes[DEVSTAT_READ] + dsp->bytes[DEVSTAT_WRITE]) / dsp->block_size; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + } + if (i < 0 && i != -1) + /* not end of indom from cache walk, some other error */ + sts = i; + } + + return sts; +#else + return PM_ERR_PMID; +#endif +} diff --git a/src/pmdas/netbsd/help b/src/pmdas/netbsd/help new file mode 100644 index 0000000..50cf1ea --- /dev/null +++ b/src/pmdas/netbsd/help @@ -0,0 +1,95 @@ +@ NETBSD.0 Instance domain for load average +Universally 3 instances, "1 minute" (1), "5 minute" (5) and +"15 minute" (15). + +@ NETBSD.1 CPU Instance domain for kernel.percpu metrics +One instance for each physical CPU. + +@ NETBSD.2 DISK Instance domain for disk.dev metrics +One instance for each physical "direct access" device. + +@ kernel.all.hz system hz rate +Microseconds per "hz" tick. +@ kernel.all.load 1, 5 and 15 minute load average +@ kernel.all.pswitch count of context switches +@ kernel.all.syscall count of system calls +@ kernel.all.intr count of interrupts serviced +@ kernel.all.cpu.user total user CPU time for all CPUs +@ kernel.all.cpu.nice total nice user CPU time for all CPUs +@ kernel.all.cpu.sys total sys CPU time for all CPUs +@ kernel.all.cpu.intr total interrupt CPU time for all CPUs +@ kernel.all.cpu.idle total idle CPU time for all CPUs +@ kernel.percpu.cpu.user user CPU time for each CPU +@ kernel.percpu.cpu.nice nice user CPU time for each CPU +@ kernel.percpu.cpu.sys sys CPU time for each CPU +@ kernel.percpu.cpu.intr interrupt CPU time for each CPU +@ kernel.percpu.cpu.idle idle CPU time for each CPU + +@ hinv.ncpu number of CPUs in the system +@ hinv.ndisk number of disks in the system +@ hinv.physmem total system memory +@ hinv.pagesize kernel page size +@ hinv.cpu.vendor system's CPU vendor +@ hinv.cpu.model system's CPU model +@ hinv.cpu.arch system's machine dependent CPU architecture type + +@ swap.length total swap space size +@ swap.used reserved (or allocated) swap space +@ swap.free available swap space + +@ swap.pagesin pages read from external storage to service page faults +@ swap.pagesout dirty pages written to swap devices +When the rate of page writes is non-zero, this is the most useful +indication severe demand for physical memory. +@ swap.in number of swap in operations +@ swap.out number of swap out operations + +@ disk.dev.read Count of read operations per disk +@ disk.dev.write Count of write operations per disk +@ disk.dev.total Count of read or write operations (IOPs) per disk +@ disk.dev.read_bytes Count of bytes read from each disk +@ disk.dev.write_bytes Count of bytes written to each disk +@ disk.dev.total_bytes Count of bytes transferred to or from each disk +@ disk.dev.blkread Count of blocks read from each disk +@ disk.dev.blkwrite Count of blocks written to each disk +@ disk.dev.blktotal Count of blocks transferred to or from each disk +@ disk.all.read Count of read operations across all disks +@ disk.all.write Count of write operations across all disks +@ disk.all.total Count of read or write operations (IOPs) across all disks +@ disk.all.read_bytes Count of bytes read from all disks +@ disk.all.write_bytes Count of bytes written to all disks +@ disk.all.total_bytes Count of bytes transferred to or from all disks +@ disk.all.blkread Count of blocks read from all disks +@ disk.all.blkwrite Count of blocks written to all disks +@ disk.all.blktotal Count of blocks transferred to or from all disks + +@ mem.util.all Total memory managed by the system +@ mem.util.used Memory that is actively in use +Equals "all" minus "free" minus "inactive" minus "cached". +@ mem.util.free Unallocated and free memory +@ mem.util.bufmem Memory associated with active buffer I/O +@ mem.util.cached Cached memory +Unmodified (clean and cached) pages from files in filesystems. +@ mem.util.wired Wired or pinned memory that cannot be paged out +@ mem.util.active Recently accessed memory +@ mem.util.inactive Memory that is in use, but has not be accessed recently +@ mem.util.avail Available memory +Free plus inactive plus cached. + +@ network.interface.mtu Maximum Transfer Unit for each network interface +@ network.interface.up "UP" state for each network interface +@ network.interface.baudrate Data baudrate for each network interface +@ network.interface.in.bytes Bytes received on each network interface +@ network.interface.in.packets Packets received on each network interface +@ network.interface.in.mcasts Multicast packets received on each network interface +@ network.interface.in.errors Input errors on each network interface +@ network.interface.in.drops Dropped packets on each network interface +@ network.interface.out.bytes Bytes transmitted on each network interface +@ network.interface.out.packets Packets transmitted on each network interface +@ network.interface.out.mcasts Multicast packets transmitted on each network interface +@ network.interface.out.errors Output errors on each network interface +@ network.interface.out.collisions Output collisions on each network interface +@ network.interface.total.bytes Bytes received or transmitted on each network interface +@ network.interface.total.packets Packets received or transmitted on each network interface +@ network.interface.total.mcasts Multicast packets received or transmitted on each network interface +@ network.interface.total.errors Input or output errors on each network interface diff --git a/src/pmdas/netbsd/netbsd.c b/src/pmdas/netbsd/netbsd.c new file mode 100644 index 0000000..d0c68bc --- /dev/null +++ b/src/pmdas/netbsd/netbsd.c @@ -0,0 +1,981 @@ +/* + * NetBSD Kernel PMDA + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2012,2013 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "domain.h" +#include "netbsd.h" + +/* static instances */ +static pmdaInstid loadav_indom[] = { + { 1, "1 minute" }, { 5, "5 minute" }, { 15, "15 minute" } +}; + +/* instance domains */ +pmdaIndom indomtab[] = { + { LOADAV_INDOM, sizeof(loadav_indom)/sizeof(loadav_indom[0]), loadav_indom }, + { CPU_INDOM, 0, NULL }, + { DISK_INDOM, 0, NULL }, + { NETIF_INDOM, 0, NULL }, +}; +static int indomtablen = sizeof(indomtab) / sizeof(indomtab[0]); + +#define CL_SYSCTL 0 +#define CL_SPECIAL 1 +#define CL_DISK 2 +#define CL_NETIF 3 + +/* + * All the PCP metrics. + * + * For sysctl metrics, m_user (the first field) is set the the PCP + * name of the metric, and during initialization this is replaced by + * a pointer to the corresponding entry in mib[] (based on matching, + * or prefix matching (see matchname()) the PCP name here with + * m_pcpname[] in the mib[] entries. + * + * cluster map + * CL_SYSCTL simple sysctl() metrics, either one metric per mib, or + * one struct per mib + * CL_SPECIAL trickier sysctl() metrics involving synthesis or arithmetic + * or other methods + * CL_DISK disk metrics + * CL_NETIF network interface metrics + */ + +static pmdaMetric metrictab[] = { + { (void *)"hinv.ncpu", + { PMDA_PMID(CL_SYSCTL,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"hinv.physmem", + { PMDA_PMID(CL_SYSCTL,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { (void *)"kernel.all.load", + { PMDA_PMID(CL_SYSCTL,2), PM_TYPE_FLOAT, LOADAV_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"kernel.all.cpu.user", + { PMDA_PMID(CL_SYSCTL,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.nice", + { PMDA_PMID(CL_SYSCTL,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.sys", + { PMDA_PMID(CL_SYSCTL,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.intr", + { PMDA_PMID(CL_SYSCTL,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.cpu.idle", + { PMDA_PMID(CL_SYSCTL,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.user", + { PMDA_PMID(CL_SYSCTL,8), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.nice", + { PMDA_PMID(CL_SYSCTL,9), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.sys", + { PMDA_PMID(CL_SYSCTL,10), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.intr", + { PMDA_PMID(CL_SYSCTL,11), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.percpu.cpu.idle", + { PMDA_PMID(CL_SYSCTL,12), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) } }, + { (void *)"kernel.all.hz", + { PMDA_PMID(CL_SYSCTL,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0) } }, + { (void *)"hinv.cpu.vendor", + { PMDA_PMID(CL_SYSCTL,15), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"hinv.cpu.model", + { PMDA_PMID(CL_SYSCTL,16), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"hinv.cpu.arch", + { PMDA_PMID(CL_SYSCTL,17), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { (void *)"swap.pagesin", + { PMDA_PMID(CL_SYSCTL,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.pagesout", + { PMDA_PMID(CL_SYSCTL,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.in", + { PMDA_PMID(CL_SYSCTL,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.in", + { PMDA_PMID(CL_SYSCTL,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"kernel.all.pswitch", + { PMDA_PMID(CL_SYSCTL,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"kernel.all.syscall", + { PMDA_PMID(CL_SYSCTL,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"kernel.all.intr", + { PMDA_PMID(CL_SYSCTL,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { (void *)"swap.length", + { PMDA_PMID(CL_SYSCTL,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { (void *)"swap.used", + { PMDA_PMID(CL_SYSCTL,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + + { NULL, /* hinv.ndisk */ + { PMDA_PMID(CL_SPECIAL,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + /* + * swap.free is the difference between sysctl variables vm.swap_total + * and vm.swap_reserved, so it is important they are kept together + * in mib[] below + */ + { (void *)"swap.length", /* swap.free */ + { PMDA_PMID(CL_SPECIAL,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* hinv.pagesize */ + { PMDA_PMID(CL_SPECIAL,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { (void *)"mem.util.all", + { PMDA_PMID(CL_SPECIAL,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + /* + * mem.util.used is computed from several of the vm.stats.vm.v_*_count + * sysctl metrics, so it is important they are kept together in mib[] + * below + */ + { (void *)"mem.util.all", /* mem.util.used */ + { PMDA_PMID(CL_SPECIAL,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.free", + { PMDA_PMID(CL_SPECIAL,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.bufmem", + { PMDA_PMID(CL_SPECIAL,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.cached", + { PMDA_PMID(CL_SPECIAL,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.wired", + { PMDA_PMID(CL_SPECIAL,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.active", + { PMDA_PMID(CL_SPECIAL,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + { (void *)"mem.util.inactive", + { PMDA_PMID(CL_SPECIAL,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + /* + * mem.util.avail is computed from several of the vm.stats.vm.v_*_count + * sysctl metrics, so it is important they are kept together in mib[] + * below + */ + { (void *)"mem.util.all", /* mem.util.avail */ + { PMDA_PMID(CL_SPECIAL,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) } }, + + { NULL, /* disk.dev.read */ + { PMDA_PMID(CL_DISK,0), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.write */ + { PMDA_PMID(CL_DISK,1), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.total */ + { PMDA_PMID(CL_DISK,2), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.read_bytes */ + { PMDA_PMID(CL_DISK,3), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.dev.write_bytes */ + { PMDA_PMID(CL_DISK,4), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.dev.total_bytes */ + { PMDA_PMID(CL_DISK,5), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.all.read */ + { PMDA_PMID(CL_DISK,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.write */ + { PMDA_PMID(CL_DISK,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.total */ + { PMDA_PMID(CL_DISK,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.read_bytes */ + { PMDA_PMID(CL_DISK,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.all.write_bytes */ + { PMDA_PMID(CL_DISK,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.all.total_bytes */ + { PMDA_PMID(CL_DISK,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* disk.dev.blkread */ + { PMDA_PMID(CL_DISK,12), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.blkwrite */ + { PMDA_PMID(CL_DISK,13), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.dev.blktotal */ + { PMDA_PMID(CL_DISK,14), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.blkread */ + { PMDA_PMID(CL_DISK,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.blkwrite */ + { PMDA_PMID(CL_DISK,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* disk.all.blktotal */ + { PMDA_PMID(CL_DISK,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + + { NULL, /* network.interface.mtu */ + { PMDA_PMID(CL_NETIF,0), PM_TYPE_U32, NETIF_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { NULL, /* network.interface.up */ + { PMDA_PMID(CL_NETIF,1), PM_TYPE_U32, NETIF_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { NULL, /* network.interface.baudrate */ + { PMDA_PMID(CL_NETIF,2), PM_TYPE_U64, NETIF_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, + { NULL, /* network.interface.in.bytes */ + { PMDA_PMID(CL_NETIF,3), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* network.interface.in.packets */ + { PMDA_PMID(CL_NETIF,4), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.in.mcasts */ + { PMDA_PMID(CL_NETIF,5), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.in.errors */ + { PMDA_PMID(CL_NETIF,6), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.in.drops */ + { PMDA_PMID(CL_NETIF,7), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.bytes */ + { PMDA_PMID(CL_NETIF,8), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* network.interface.out.packets */ + { PMDA_PMID(CL_NETIF,9), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.mcasts */ + { PMDA_PMID(CL_NETIF,10), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.errors */ + { PMDA_PMID(CL_NETIF,11), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.out.collisions */ + { PMDA_PMID(CL_NETIF,12), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.total.bytes */ + { PMDA_PMID(CL_NETIF,13), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) } }, + { NULL, /* network.interface.total.packets */ + { PMDA_PMID(CL_NETIF,14), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.total.mcasts */ + { PMDA_PMID(CL_NETIF,15), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { NULL, /* network.interface.total.errors */ + { PMDA_PMID(CL_NETIF,16), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +}; +static int metrictablen = sizeof(metrictab) / sizeof(metrictab[0]); + +/* + * mapping between PCP metrics and sysctl metrics + * + * initialization note ... all elments after m_pcpname and m_name are OK + * to be 0 or NULL + */ +typedef struct { + const char *m_pcpname; /* PCP metric name or prefix (see matchname() */ + const char *m_name; /* sysctl metric name */ + size_t m_miblen; /* number of elements in m_mib[] */ + int *m_mib; + int m_fetched; /* 1 if m_data is current and valid */ + size_t m_datalen; /* number of bytes in m_data[] */ + void *m_data; /* value from sysctl */ +} mib_t; +static mib_t map[] = { + { "hinv.ncpu", "hw.ncpu" }, + { "hinv.physmem", "hw.physmem" }, + { "hinv.cpu.vendor", "hw.machine" }, + { "hinv.cpu.model", "hw.model" }, + { "hinv.cpu.arch", "hw.machine_arch" }, + { "kernel.all.load", "vm.loadavg" }, + { "kernel.all.hz", "kern.clockrate" }, + { "kernel.all.cpu.*", "kern.cp_time" }, + // need special logic for kern.cp_time.0 ? { "kernel.percpu.cpu.*", "kern.cp_times." }, + // not here? { "swap.pagesin", "vm.stats.vm.v_swappgsin" }, + // not here? { "swap.pagesout", "vm.stats.vm.v_swappgsout" }, + // not here? { "swap.in", "vm.stats.vm.v_swapin" }, + { "swap.out", "vm.swapout" }, + // not here? { "kernel.all.pswitch", "vm.stats.sys.v_swtch" }, + // not here? { "kernel.all.syscall", "vm.stats.sys.v_syscall" }, + // not here? { "kernel.all.intr", "vm.stats.sys.v_intr" }, + { "mem.util.bufmem", "vfs.bufspace" }, +/* + * DO NOT MOVE next 2 entries ... see note above for swap.free + */ + // not here? { "swap.length", "vm.swap_total" }, + // not here? { "swap.used", "vm.swap_reserved" }, +/* + * DO NOT MOVE next 6 entries ... see note above for mem.util.avail + * and mem.util.used + */ + // not here? { "mem.util.all", "vm.stats.vm.v_page_count" }, + // not here? { "mem.util.free", "vm.stats.vm.v_free_count" }, + // not here? { "mem.util.cached", "vm.stats.vm.v_cache_count" }, + // not here? { "mem.util.wired", "vm.stats.vm.v_wire_count" }, + // not here? { "mem.util.active", "vm.stats.vm.v_active_count" }, + // not here? { "mem.util.inactive", "vm.stats.vm.v_inactive_count" }, + +}; +static int maplen = sizeof(map) / sizeof(map[0]); +static mib_t bad_mib = { "bad.mib", "bad.mib", 0, NULL, 0, 0, NULL }; + +static char *username; +static int isDSO = 1; /* =0 I am a daemon */ +static int cpuhz; /* frequency for CPU time metrics */ +static int ncpu; /* number of cpus in kern.cp_times.* data */ +static int pagesize; /* vm page size */ + +/* + * Fetch values from sysctl() + * + * Expect the result to be xpect bytes to match the PCP data size or + * anticipated structure size, unless xpect is ==0 in which case the + * size test is skipped. + */ +static int +do_sysctl(mib_t *mp, size_t xpect) +{ + /* + * Note zero trip if mp->m_data and mp->datalen are already valid + * and current + */ + for ( ; mp->m_fetched == 0; ) { + int sts; + sts = sysctl(mp->m_mib, (u_int)mp->m_miblen, mp->m_data, &mp->m_datalen, NULL, 0); + fprintf(stderr, "sysctl(%s%s) -> %d (datalen=%d)\n", mp->m_name, mp->m_data == NULL ? " firstcall" : "", sts, (int)mp->m_datalen); + if (sts == 0 && mp->m_data != NULL) { + mp->m_fetched = 1; + break; + } + if ((sts == -1 && errno == ENOMEM) || (sts == 0 && mp->m_data == NULL)) { + /* first call for this one, or data changed size */ + mp->m_data = realloc(mp->m_data, mp->m_datalen); + if (mp->m_data == NULL) { + fprintf(stderr, "Error: %s: buffer alloc failed for sysctl metric \"%s\"\n", mp->m_pcpname, mp->m_name); + __pmNoMem("do_sysctl", mp->m_datalen, PM_FATAL_ERR); + /*NOTREACHED*/ + } + } + else + return -errno; + } + if (xpect > 0 && mp->m_datalen != xpect) { + fprintf(stderr, "Error: %s: sysctl(%s) datalen=%d not %d!\n", mp->m_pcpname, mp->m_name, (int)mp->m_datalen, (int)xpect); + return 0; + } + return mp->m_datalen; +} + +/* + * kernel memory reader setup + */ +struct nlist symbols[] = { + { .n_name = "_ifnet" }, + { .n_name = NULL } +}; +kvm_t *kvmp; + +static void +kmemread_init(void) +{ + int sts; + int i; + char errmsg[_POSIX2_LINE_MAX]; + + /* + * If we're running as a daemon PMDA, assume we're setgid kmem, + * so we can open /dev/kmem, and downgrade privileges after the + * kvm_open(). + * For a DSO PMDA, we have to assume pmcd has the required + * privileges and don't dink with them. + */ + kvmp = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errmsg); + if (!isDSO) + setgid(getgid()); + if (kvmp == NULL) { + fprintf(stderr, "kmemread_init: kvm_openfiles failed: %s\n", errmsg); + return; + } + + sts = kvm_nlist(kvmp, symbols); + if (sts < 0) { + fprintf(stderr, "kmemread_init: kvm_nlist failed: %s\n", pmErrStr(-errno)); + for (i = 0; i < sizeof(symbols)/sizeof(symbols[0])-1; i++) + symbols[i].n_value = 0; + return; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + for (i = 0; i < sizeof(symbols)/sizeof(symbols[0])-1; i++) { + fprintf(stderr, "Info: kernel symbol %s found at 0x%08lx\n", symbols[i].n_name, symbols[i].n_value); + } + } +#endif + +} + +/* + * Callback provided to pmdaFetch ... come here once per metric-instance + * pair in each pmFetch(). + */ +static int +netbsd_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + int sts = PM_ERR_PMID; + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + mib_t *mp; + + mp = (mib_t *)mdesc->m_user; + if (idp->cluster == CL_SYSCTL) { + /* sysctl() simple cases */ + switch (idp->item) { + /* 32-bit integer values */ + case 0: /* hinv.ncpu */ + case 18: /* swap.pagesin */ + case 19: /* swap.pagesout */ + case 20: /* swap.in */ + case 21: /* swap.out */ + case 22: /* kernel.all.pswitch */ + case 23: /* kernel.all.syscall */ + case 24: /* kernel.all.intr */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data); + sts = 1; + } + break; + + /* 64-bit integer values */ + case 1: /* hinv.physmem */ + case 25: /* swap.length */ + case 26: /* swap.used */ + sts = do_sysctl(mp, sizeof(atom->ull)); + if (sts > 0) { + atom->ull = *((__uint64_t *)mp->m_data); + sts = 1; + } + break; + + /* string values */ + case 15: /* hinv.cpu.vendor */ + case 16: /* hinv.cpu.model */ + case 17: /* hinv.cpu.arch */ + sts = do_sysctl(mp, (size_t)0); + if (sts > 0) { + atom->cp = (char *)mp->m_data; + sts = 1; + } + break; + + /* structs and aggregates */ + case 2: /* kernel.all.load */ + sts = do_sysctl(mp, 3*sizeof(atom->d)); + if (sts > 0) { + int i; + struct loadavg *lp = (struct loadavg *)mp->m_data; + if (inst == 1) + i = 0; + else if (inst == 5) + i = 1; + else if (inst == 15) + i = 2; + else + return PM_ERR_INST; + atom->f = (float)((double)lp->ldavg[i] / lp->fscale); + sts = 1; + } + break; + + case 3: /* kernel.all.cpu.user */ + case 4: /* kernel.all.cpu.nice */ + case 5: /* kernel.all.cpu.sys */ + case 6: /* kernel.all.cpu.intr */ + case 7: /* kernel.all.cpu.idle */ + sts = do_sysctl(mp, CPUSTATES*sizeof(atom->ull)); + if (sts > 0) { + /* + * PMID assignment is important in the "-3" below so + * that metrics map to consecutive elements of the + * returned value in the order defined for CPUSTATES, + * i.e. CP_USER, CP_NICE, CP_SYS, CP_INTR and + * CP_IDLE + */ + atom->ull = 1000*((__uint64_t *)mp->m_data)[idp->item-3]/cpuhz; + sts = 1; + } + break; + + case 8: /* kernel.percpu.cpu.user */ + case 9: /* kernel.percpu.cpu.nice */ + case 10: /* kernel.percpu.cpu.sys */ + case 11: /* kernel.percpu.cpu.intr */ + case 12: /* kernel.percpu.cpu.idle */ + sts = do_sysctl(mp, ncpu*CPUSTATES*sizeof(atom->ull)); + if (sts > 0) { + /* + * PMID assignment is important in the "-8" below so + * that metrics map to consecutive elements of the + * returned value in the order defined for CPUSTATES, + * i.e. CP_USER, CP_NICE, CP_SYS, CP_INTR and + * CP_IDLE, and then there is one such set for each + * CPU up to the maximum number of CPUs installed in + * the system. + */ + atom->ull = 1000*((__uint64_t *)mp->m_data)[inst * CPUSTATES + idp->item-8]/cpuhz; + sts = 1; + } + break; + + case 13: /* kernel.all.hz */ + sts = do_sysctl(mp, sizeof(struct clockinfo)); + if (sts > 0) { + struct clockinfo *cp = (struct clockinfo *)mp->m_data; + atom->ul = cp->hz; + sts = 1; + } + break; + + } + } + else if (idp->cluster == CL_SPECIAL) { + /* special cases */ + switch (idp->item) { + case 0: /* hinv.ndisk */ + refresh_disk_metrics(); + atom->ul = pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_SIZE_ACTIVE); + sts = 1; + break; + + case 1: /* swap.free */ + /* first vm.swap_total */ + sts = do_sysctl(mp, sizeof(atom->ull)); + if (sts > 0) { + atom->ull = *((__uint64_t *)mp->m_data); + /* + * now subtract vm.swap_reserved ... assumes consecutive + * mib[] entries + */ + mp++; + sts = do_sysctl(mp, sizeof(atom->ull)); + if (sts > 0) { + atom->ull -= *((__uint64_t *)mp->m_data); + sts = 1; + } + } + break; + + case 3: /* hinv.pagesize */ + atom->ul = pagesize; + sts = 1; + break; + + case 4: /* mem.util.all */ + case 6: /* mem.util.free */ + case 8: /* mem.util.cached */ + case 9: /* mem.util.wired */ + case 10: /* mem.util.active */ + case 11: /* mem.util.inactive */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data) * (pagesize / 1024); + sts = 1; + } + break; + + case 7: /* mem.util.bufmem */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data) / 1024; + sts = 1; + } + break; + + case 5: /* mem.util.used */ + /* + * mp-> v_page_count entry in mib[] + * assuming consecutive mib[] entries, we want + * v_page_count mp[0] - v_free_count mp[1] - + * v_cache_count mp[2] - v_inactive_count mp[5] + */ + sts = do_sysctl(mp, sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp->m_data); + sts = do_sysctl(&mp[1], sizeof(atom->ul)); + if (sts > 0) { + atom->ul -= *((__uint32_t *)mp[1].m_data); + sts = do_sysctl(&mp[2], sizeof(atom->ul)); + if (sts > 0) { + atom->ul -= *((__uint32_t *)mp[2].m_data); + sts = do_sysctl(&mp[5], sizeof(atom->ul)); + if (sts > 0) { + atom->ul -= *((__uint32_t *)mp[5].m_data); + atom->ul *= (pagesize / 1024); + sts = 1; + } + } + } + } + break; + + case 12: /* mem.util.avail */ + /* + * mp-> v_page_count entry in mib[] + * assuming consecutive mib[] entries, we want + * v_free_count mp[1] + v_cache_count mp[2] + + * v_inactive_count mp[5] + */ + sts = do_sysctl(&mp[1], sizeof(atom->ul)); + if (sts > 0) { + atom->ul = *((__uint32_t *)mp[1].m_data); + sts = do_sysctl(&mp[2], sizeof(atom->ul)); + if (sts > 0) { + atom->ul += *((__uint32_t *)mp[2].m_data); + sts = do_sysctl(&mp[5], sizeof(atom->ul)); + if (sts > 0) { + atom->ul += *((__uint32_t *)mp[5].m_data); + atom->ul *= (pagesize / 1024); + sts = 1; + } + } + } + break; + + } + } + else if (idp->cluster == CL_DISK) { + /* disk metrics */ + sts = do_disk_metrics(mdesc, inst, atom); + } + else if (idp->cluster == CL_NETIF) { + /* network interface metrics */ + sts = do_netif_metrics(mdesc, inst, atom); + } + + return sts; +} + +/* + * wrapper for pmdaFetch ... force value caches to be reloaded if needed, + * then do the fetch + */ +static int +netbsd_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; + int done_disk = 0; + int done_netif = 0; + + for (i = 0; i < maplen; i++) { + map[i].m_fetched = 0; + } + + /* + * pre-fetch all metrics if needed, and update instance domains if + * they have changed + */ + for (i = 0; !done_disk && !done_netif && i < numpmid; i++) { + if (pmid_cluster(pmidlist[i]) == CL_DISK) { + refresh_disk_metrics(); + done_disk = 1; + } + else if (pmid_cluster(pmidlist[i]) == CL_NETIF) { + refresh_netif_metrics(); + done_netif = 1; + } + } + + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * wrapper for pmdaInstance ... refresh required instance domain first + */ +static int +netbsd_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + /* + * indomtab[] instance names and ids are not used for some indoms, + * ensure pmdaCache is current + */ + if (indom == indomtab[DISK_INDOM].it_indom) + refresh_disk_metrics(); + if (indom == indomtab[NETIF_INDOM].it_indom) + refresh_netif_metrics(); + + return pmdaInstance(indom, inst, name, result, pmda); +} + +/* + * PCP metric name matching for linking metrictab[] entries to mib[] + * entries. + * + * Return 1 if prefix[] is equal to, or a prefix of name[] + * + * prefix[] of the form "a.bc" or "a.bc*" matches a name[] like "a.bc" + * or "a.bcanything", to improve readability of the initializers in + * mib[], and asterisk is a "match all" special case, so "a.b.*" matches + * "a.b.anything" + */ +static int +matchname(const char *prefix, const char *name) +{ + while (*prefix != '\0' && *name != '\0' && *prefix == *name) { + prefix++; + name++; + } + if (*prefix == '\0' || *prefix == '*') + return 1; + else + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + * + * Do mapping from sysclt(3) names to mibs. + * Collect some global constants. + * Build the system-specific, but not dynamic, instance domains, + * e.g. CPU_INDOM. + * Initialize the kernel memory reader. + */ +void +netbsd_init(pmdaInterface *dp) +{ + int i; + int m; + int sts; + struct clockinfo clockrates; + size_t sz; + int mib[CTL_MAXNAME]; /* enough for longest mib key */ + char iname[16]; /* enough for cpuNN.. */ + + if (isDSO) { + char mypath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "netbsd" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_5, "netbsd DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.four.fetch = netbsd_fetch; + dp->version.four.instance = netbsd_instance; + + pmdaSetFetchCallBack(dp, netbsd_fetchCallBack); + + pmdaInit(dp, indomtab, indomtablen, metrictab, metrictablen); + + /* + * Link metrictab[] entries via m_user to map[] entries based on + * matching sysctl(3) name + * + * also translate the sysctl(3) name to a mib + */ + for (m = 0; m < metrictablen; m++) { + if (metrictab[m].m_user == NULL) { + /* not using sysctl(3) */ + continue; + } + for (i = 0; i < maplen; i++) { + if (matchname(map[i].m_pcpname, (char *)metrictab[m].m_user)) { + if (map[i].m_mib == NULL) { + /* + * multiple metrictab[] entries may point to the same + * mib[] entry, but this is the first time for this + * mib[] entry ... + */ + map[i].m_miblen = sizeof(mib); + sts = sysctlnametomib(map[i].m_name, mib, &map[i].m_miblen); + if (sts == 0) { + map[i].m_mib = (int *)malloc(map[i].m_miblen*sizeof(map[i].m_mib[0])); + if (map[i].m_mib == NULL) { + fprintf(stderr, "Error: %s (%s): failed mib alloc for sysctl metric \"%s\"\n", map[i].m_pcpname, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name); + __pmNoMem("netbsd_init: mib", map[i].m_miblen*sizeof(map[i].m_mib[0]), PM_FATAL_ERR); + /*NOTREACHED*/ + } + memcpy(map[i].m_mib, mib, map[i].m_miblen*sizeof(map[i].m_mib[0])); + } + else { + fprintf(stderr, "Error: %s (%s): failed sysctlnametomib(\"%s\", ...): %s\n", map[i].m_pcpname, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name, pmErrStr(-errno)); + metrictab[m].m_user = (void *)&bad_mib; + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + int p; + fprintf(stderr, "Info: %s (%s): sysctl metric \"%s\" -> ", (char *)metrictab[m].m_user, pmIDStr(metrictab[m].m_desc.pmid), map[i].m_name); + for (p = 0; p < map[i].m_miblen; p++) { + if (p > 0) fputc('.', stderr); + fprintf(stderr, "%d", map[i].m_mib[p]); + } + fputc('\n', stderr); + } +#endif + metrictab[m].m_user = (void *)&map[i]; + break; + } + } + if (i == maplen) { + fprintf(stderr, "Error: %s (%s): cannot match name in sysctl map[]\n", (char *)metrictab[m].m_user, pmIDStr(metrictab[m].m_desc.pmid)); + metrictab[m].m_user = (void *)&bad_mib; + } + } + + /* + * Collect some global constants needed later ... + */ + sz = sizeof(clockrates); + sts = sysctlbyname("kern.clockrate", &clockrates, &sz, NULL, 0); + if (sts < 0) { + fprintf(stderr, "Fatal Error: sysctlbyname(\"kern.clockrate\", ...) failed: %s\n", pmErrStr(-errno)); + exit(1); + } + cpuhz = clockrates.stathz; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Info: CPU time \"hz\" = %d\n", cpuhz); +#endif + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + sz = sizeof(ncpu); + sts = sysctl(mib, 2, &ncpu, &sz, NULL, 0); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Info: ncpu = %d\n", ncpu); +#endif + + sz = sizeof(pagesize); + sts = sysctlbyname("hw.pagesize", &pagesize, &sz, NULL, 0); + if (sts < 0) { + fprintf(stderr, "Fatal Error: sysctlbyname(\"hw.pagesize\", ...) failed: %s\n", pmErrStr(-errno)); + exit(1); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Info: VM pagesize = %d\n", pagesize); +#endif + + /* + * Build some instance domains ... + */ + indomtab[CPU_INDOM].it_numinst = ncpu; + indomtab[CPU_INDOM].it_set = (pmdaInstid *)malloc(ncpu * sizeof(pmdaInstid)); + if (indomtab[CPU_INDOM].it_set == NULL) { + __pmNoMem("netbsd_init: CPU_INDOM it_set", ncpu * sizeof(pmdaInstid), PM_FATAL_ERR); + /*NOTREACHED*/ + } + for (i = 0; i < ncpu; i++) { + indomtab[CPU_INDOM].it_set[i].i_inst = i; + snprintf(iname, sizeof(iname), "cpu%d", i); + indomtab[CPU_INDOM].it_set[i].i_name = strdup(iname); + if (indomtab[CPU_INDOM].it_set[i].i_name == NULL) { + __pmNoMem("netbsd_init: CPU_INDOM strdup iname", strlen(iname), PM_FATAL_ERR); + /*NOTREACHED*/ + } + } + + kmemread_init(); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n\n", pmProgname); + fputs("Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -U username user account to run under (default \"pcp\")\n" + "\nExactly one of the following options may appear:\n" + " -i port expect PMCD to connect on given inet port (number or name)\n" + " -p expect PMCD to supply stdin/stdout (pipe)\n" + " -u socket expect PMCD to connect on given unix domain socket\n" + " -6 port expect PMCD to connect on given ipv6 port (number or name)\n", + stderr); + exit(1); +} + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int c, err = 0; + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char mypath[MAXPATHLEN]; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "netbsd" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_5, pmProgname, NETBSD, + "netbsd.log", mypath); + + while ((c = pmdaGetOpt(argc, argv, "D:d:i:l:pu:U:6:?", &dispatch, &err)) != EOF) { + switch(c) { + case 'U': + username = optarg; + break; + default: + err++; + } + } + if (err) + usage(); + + pmdaOpenLog(&dispatch); + netbsd_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/netbsd/netbsd.h b/src/pmdas/netbsd/netbsd.h new file mode 100644 index 0000000..c6b9b72 --- /dev/null +++ b/src/pmdas/netbsd/netbsd.h @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2012 Ken McDonell All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * instance domains control + */ +#define LOADAV_INDOM 0 +#define CPU_INDOM 1 +#define DISK_INDOM 2 +#define NETIF_INDOM 3 +extern pmdaIndom indomtab[]; + +extern void refresh_disk_metrics(void); +extern int do_disk_metrics(pmdaMetric *, unsigned int, pmAtomValue *); + +extern void refresh_netif_metrics(void); +extern int do_netif_metrics(pmdaMetric *, unsigned int, pmAtomValue *); + +/* + * kernel memory reader pieces + */ +#include +#include + +extern kvm_t *kvmp; + +#define KERN_IFNET 0 +extern struct nlist symbols[]; diff --git a/src/pmdas/netbsd/netif.c b/src/pmdas/netbsd/netif.c new file mode 100644 index 0000000..19737bb --- /dev/null +++ b/src/pmdas/netbsd/netif.c @@ -0,0 +1,233 @@ +/* + * NetBSD Kernel PMDA - network interface metrics + * + * Copyright (c) 2012,2013 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#if 0 +#include +#include +#include +#include +#include +#endif + +#include "netbsd.h" + +#define WARN_INIT 1 +#define WARN_READ_HEAD 2 + +void +refresh_netif_metrics(void) +{ +#if 0 + int i; + int sts; + unsigned long kaddr; + struct ifnethead ifnethead; + struct ifnet ifnet; + struct ifnet *ifp; + static int warn = 0; /* warn once control */ + + /* + * Not sure that the order of chained netif structs is invariant, + * especially if interfaces are added to the configuration after + * initial system boot ... so mark all the instances as inactive + * and re-match based on the interface name + */ + pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_INACTIVE); + + kaddr = symbols[KERN_IFNET].n_value; + if (kvmp == NULL || kaddr == 0) { + /* no network interface metrics for us today ... */ + if ((warn & WARN_INIT) == 0) { + fprintf(stderr, "refresh_netif_metrics: Warning: cannot get any network interface metrics\n"); + warn |= WARN_INIT; + } + return; + } + + /* + * Kernel data structures for the linked list of network interface + * information. + * + * _ifnet -> struct ifnethead { + * struct ifnet *tqh_first; + * struct ifnet **tqh_last; + * ... + * } + * + * and within an ifnet struct (declared in ) we find + * the linked list maintained in if_link, the external interface + * name in if_xname[] and if_data which is a nested if_data stuct + * (declared in ) that contains many of the goodies we're + * after, e.g. u_char ifi_type, u_long ifi_mtu, u_long ifi_baudrate, + * u_long ifi_ipackets, u_long ifi_opackets, u_long ifi_ibytes, + * u_long ifi_obytes, etc. + */ + if (kvm_read(kvmp, kaddr, (char *)&ifnethead, sizeof(ifnethead)) != sizeof(ifnethead)) { + if ((warn & WARN_READ_HEAD) == 0) { + fprintf(stderr, "refresh_netif_metrics: Warning: kvm_read: ifnethead: %s\n", kvm_geterr(kvmp)); + warn |= WARN_READ_HEAD; + } + return; + } + + for (i = 0; ; i++) { + if (i == 0) + kaddr = (unsigned long)TAILQ_FIRST(&ifnethead); + else + kaddr = (unsigned long)TAILQ_NEXT(&ifnet, if_link); + + if (kaddr == 0) + break; + + if (kvm_read(kvmp, kaddr, (char *)&ifnet, sizeof(ifnet)) != sizeof(ifnet)) { + fprintf(stderr, "refresh_netif_metrics: Error: kvm_read: ifnet[%d]: %s\n", i, kvm_geterr(kvmp)); + return; + } + + /* skip network interfaces that are not interesting ... */ + if (strcmp(ifnet.if_xname, "lo0") == 0) + continue; + + sts = pmdaCacheLookupName(indomtab[NETIF_INDOM].it_indom, ifnet.if_xname, NULL, (void **)&ifp); + if (sts == PMDA_CACHE_ACTIVE) { + fprintf(stderr, "refresh_netif_metrics: Warning: duplicate name (%s) in network interface indom\n", ifnet.if_xname); + continue; + } + else if (sts == PMDA_CACHE_INACTIVE) { + /* reactivate an existing entry */ + pmdaCacheStore(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_ADD, ifnet.if_xname, (void *)ifp); + } + else { + /* new entry */ + ifp = (struct ifnet *)malloc(sizeof(*ifp)); + if (ifp == NULL) { + fprintf(stderr, "Error: struct ifnet alloc failed for network interface \"%s\"\n", ifnet.if_xname); + __pmNoMem("refresh_netif_metrics", sizeof(*ifp), PM_FATAL_ERR); + /*NOTREACHED*/ + } + pmdaCacheStore(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_ADD, ifnet.if_xname, (void *)ifp); + } + memcpy((void *)ifp, (void *)&ifnet, sizeof(*ifp)); + } +#endif +} + +int +do_netif_metrics(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ +#if 0 + struct ifnet *ifp; + int sts; + + if (inst != PM_IN_NULL) { + /* + * per-network interface metrics + */ + sts = pmdaCacheLookup(indomtab[NETIF_INDOM].it_indom, inst, NULL, (void **)&ifp); + if (sts == PMDA_CACHE_ACTIVE) { + sts = 1; + /* cluster and domain already checked, just need item ... */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 0: /* network.interface.mtu */ + atom->ul = (__uint32_t)ifp->if_data.ifi_mtu; + break; + + case 1: /* network.interface.up */ + atom->ul = (ifp->if_flags & IFF_UP) == IFF_UP; + break; + + case 2: /* network.interface.baudrate */ + atom->ull = ifp->if_data.ifi_baudrate; + break; + + case 3: /* network.interface.in.bytes */ + atom->ull = ifp->if_data.ifi_ibytes; + break; + + case 4: /* network.interface.in.packets */ + atom->ull = ifp->if_data.ifi_ipackets; + break; + + case 5: /* network.interface.in.mcasts */ + atom->ull = ifp->if_data.ifi_imcasts; + break; + + case 6: /* network.interface.in.errors */ + atom->ull = ifp->if_data.ifi_ierrors; + break; + + case 7: /* network.interface.in.drops */ + atom->ull = ifp->if_data.ifi_iqdrops; + break; + + case 8: /* network.interface.out.bytes */ + atom->ull = ifp->if_data.ifi_obytes; + break; + + case 9: /* network.interface.out.packets */ + atom->ull = ifp->if_data.ifi_opackets; + break; + + case 10: /* network.interface.out.mcasts */ + atom->ull = ifp->if_data.ifi_omcasts; + break; + + case 11: /* network.interface.out.errors */ + atom->ull = ifp->if_data.ifi_oerrors; + break; + + case 12: /* network.interface.out.collisions */ + atom->ull = ifp->if_data.ifi_collisions; + break; + + case 13: /* network.interface.total.bytes */ + atom->ull = ifp->if_data.ifi_ibytes + ifp->if_data.ifi_obytes; + break; + + case 14: /* network.interface.total.packets */ + atom->ull = ifp->if_data.ifi_ipackets + ifp->if_data.ifi_opackets; + break; + + case 15: /* network.interface.total.mcasts */ + atom->ull = ifp->if_data.ifi_imcasts + ifp->if_data.ifi_omcasts; + break; + + case 16: /* network.interface.total.errors */ + atom->ull = ifp->if_data.ifi_ierrors + ifp->if_data.ifi_oerrors; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else + sts = 0; + } + + return sts; +#else + return PM_ERR_PMID; +#endif +} diff --git a/src/pmdas/netbsd/root_netbsd b/src/pmdas/netbsd/root_netbsd new file mode 100644 index 0000000..c05ce4a --- /dev/null +++ b/src/pmdas/netbsd/root_netbsd @@ -0,0 +1,172 @@ +/* + * Metrics for NetBSD kernel PMDA + * + * Copyright (c) 2012 Ken McDonell All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * the domain for the NETBSD PMDA ... + */ +#ifndef NETBSD +#define NETBSD 116 +#endif + +root { + hinv + kernel + disk + mem + network + swap +} + +hinv { + ncpu NETBSD:0:0 + ndisk NETBSD:1:0 + physmem NETBSD:0:1 + pagesize NETBSD:1:3 + cpu +} + +hinv.cpu { + vendor NETBSD:0:15 + model NETBSD:0:16 + arch NETBSD:0:17 +} + +kernel { + all + percpu +} + +kernel.all { + pswitch NETBSD:0:22 + syscall NETBSD:0:23 + intr NETBSD:0:24 + hz NETBSD:0:13 + load NETBSD:0:2 + cpu +} + +kernel.all.cpu { + user NETBSD:0:3 + nice NETBSD:0:4 + sys NETBSD:0:5 + intr NETBSD:0:6 + idle NETBSD:0:7 +} + +kernel.percpu { + cpu +} + +kernel.percpu.cpu { + user NETBSD:0:8 + nice NETBSD:0:9 + sys NETBSD:0:10 + intr NETBSD:0:11 + idle NETBSD:0:12 +} + +disk { + dev + all +} + +disk.dev { + read NETBSD:2:0 + write NETBSD:2:1 + total NETBSD:2:2 + read_bytes NETBSD:2:3 + write_bytes NETBSD:2:4 + total_bytes NETBSD:2:5 + blkread NETBSD:2:12 + blkwrite NETBSD:2:13 + blktotal NETBSD:2:14 +} + +disk.all { + read NETBSD:2:6 + write NETBSD:2:7 + total NETBSD:2:8 + read_bytes NETBSD:2:9 + write_bytes NETBSD:2:10 + total_bytes NETBSD:2:11 + blkread NETBSD:2:15 + blkwrite NETBSD:2:16 + blktotal NETBSD:2:17 +} + +mem { + util +} + +mem.util { + all NETBSD:1:4 + used NETBSD:1:5 + free NETBSD:1:6 + bufmem NETBSD:1:7 + cached NETBSD:1:8 + wired NETBSD:1:9 + active NETBSD:1:10 + inactive NETBSD:1:11 + avail NETBSD:1:12 +} + +network { + interface +} + +network.interface { + mtu NETBSD:3:0 + up NETBSD:3:1 + baudrate NETBSD:3:2 + in + out + total +} + +network.interface.in { + bytes NETBSD:3:3 + packets NETBSD:3:4 + mcasts NETBSD:3:5 + errors NETBSD:3:6 + drops NETBSD:3:7 +} + +network.interface.out { + bytes NETBSD:3:8 + packets NETBSD:3:9 + mcasts NETBSD:3:10 + errors NETBSD:3:11 + collisions NETBSD:3:12 +} + +network.interface.total { + bytes NETBSD:3:13 + packets NETBSD:3:14 + mcasts NETBSD:3:15 + errors NETBSD:3:16 +} + +swap { + pagesin NETBSD:0:18 + pagesout NETBSD:0:19 + in NETBSD:0:20 + out NETBSD:0:21 + length NETBSD:0:25 + used NETBSD:0:26 + free NETBSD:1:1 +} + +#undef NETBSD diff --git a/src/pmdas/netfilter/GNUmakefile b/src/pmdas/netfilter/GNUmakefile new file mode 100644 index 0000000..53e9051 --- /dev/null +++ b/src/pmdas/netfilter/GNUmakefile @@ -0,0 +1,55 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = netfilter +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl pmlogconf.config pmlogconf.summary +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary + $(INSTALL) -m 644 pmlogconf.config $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/config +else +install: +endif + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/netfilter/Install b/src/pmdas/netfilter/Install new file mode 100755 index 0000000..621f639 --- /dev/null +++ b/src/pmdas/netfilter/Install @@ -0,0 +1,33 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the NetFilter PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=netfilter +perl_opt=true +daemon_opt=false +forced_restart=false + +if ! test -d /proc/sys/net/ipv4/netfilter; then + echo "IP connection tracking not enabled in your kernel" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/netfilter/Remove b/src/pmdas/netfilter/Remove new file mode 100755 index 0000000..77f002c --- /dev/null +++ b/src/pmdas/netfilter/Remove @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the NetFilter PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=netfilter + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/netfilter/pmdanetfilter.pl b/src/pmdas/netfilter/pmdanetfilter.pl new file mode 100644 index 0000000..21aafa0 --- /dev/null +++ b/src/pmdas/netfilter/pmdanetfilter.pl @@ -0,0 +1,100 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +my $pmda = PCP::PMDA->new('netfilter', 97); +my $procfs = '/proc/sys/net/ipv4/'; + +sub netfilter_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ($path, $name, $value, $fh, @vals); + + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + if (!defined($metric_name)) { return (PM_ERR_PMID, 0); } + + $metric_name =~ s/\./\//; + $name = $procfs . $metric_name; + open($fh, $name) || return (PM_ERR_APPVERSION, 0); + $value = <$fh>; + close $fh; + chomp $value; + + return ($value, 1); +} + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'netfilter.ip_conntrack_max', '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'netfilter.ip_conntrack_count', '', ''); + +$pmda->set_fetch_callback(\&netfilter_fetch_callback); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdanetfilter - Linux netfilter IP connection tracking performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +exports metric values from IP connection tracking module in the Linux +kernel. + +=head1 INSTALLATION + +If you want access to the names and values for the netfilter performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/netfilter + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/netfilter + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/netfilter/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/netfilter/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/netfilter.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/netfilter/pmlogconf.config b/src/pmdas/netfilter/pmlogconf.config new file mode 100644 index 0000000..d699905 --- /dev/null +++ b/src/pmdas/netfilter/pmlogconf.config @@ -0,0 +1,5 @@ +#pmlogconf-setup 2.0 +ident Netfilter configuration +probe netfilter.ip_conntrack_max exists ? include : exclude +delta once + netfilter.ip_conntrack_max diff --git a/src/pmdas/netfilter/pmlogconf.summary b/src/pmdas/netfilter/pmlogconf.summary new file mode 100644 index 0000000..91669c4 --- /dev/null +++ b/src/pmdas/netfilter/pmlogconf.summary @@ -0,0 +1,4 @@ +#pmlogconf-setup 2.0 +ident Netfilter summary information +probe netfilter.ip_conntrack_count exists ? include : exclude + netfilter.ip_conntrack_count diff --git a/src/pmdas/news/GNUmakefile b/src/pmdas/news/GNUmakefile new file mode 100644 index 0000000..cd58095 --- /dev/null +++ b/src/pmdas/news/GNUmakefile @@ -0,0 +1,53 @@ +#!gmake +# +# Copyright (c) 2000-2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = news +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove README active pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + $(INSTALL) -m 644 README active $(PMDADIR) + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/news/Install b/src/pmdas/news/Install new file mode 100644 index 0000000..79032ea --- /dev/null +++ b/src/pmdas/news/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the news PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=news +perl_opt=true +daemon_opt=false +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/news/README b/src/pmdas/news/README new file mode 100644 index 0000000..6f8466b --- /dev/null +++ b/src/pmdas/news/README @@ -0,0 +1,58 @@ +Usenet News PMDA +================ + +This PMDA is a sample, that illustrates how a simple PMDA might be +constructed using the Perl PMDA API. + +Although the metrics supported are simple, the framework in the script +pmdanews is quite general, and could be re-used to implement other +PMDAs. + +Metrics +======= + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT news + +Installation +============ + + + # cd $PCP_PMDAS_DIR/news + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + + By default the script pmdanews operates on a small test-case copy a + news "active" file, installed from here into + $PCP_PMDAS_DIR/news/active. If you wish to use a more realistic base + of Usenet data, edit pmdanews.pl to make it use your "live active" + groups, then + + # ./Install. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/news + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/news.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/news/Remove b/src/pmdas/news/Remove new file mode 100644 index 0000000..a14cbc2 --- /dev/null +++ b/src/pmdas/news/Remove @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the news PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=news + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/news/active b/src/pmdas/news/active new file mode 100644 index 0000000..30ce528 --- /dev/null +++ b/src/pmdas/news/active @@ -0,0 +1,12 @@ +# fake for testing +# the metrics should be as follows +# news.articles.total 1000 +# news.articles.last 100 0 0 200 1101 3795 7130 +# news.articles.count 0 0 0 100 100 700 100 +comp.sys.sgi 0100 0100 y +comp.sys.sgi.graphics 0000 0000 y +comp.sys.sgi.hardware 0000 0000 y +sgi.bad-attitude 3795 3095 y +sgi.bugs.sherwood 1101 1001 y +sgi.engr.all 0200 0100 y +sgi.general 7130 7030 y diff --git a/src/pmdas/news/pmdanews.pl b/src/pmdas/news/pmdanews.pl new file mode 100644 index 0000000..9d59178 --- /dev/null +++ b/src/pmdas/news/pmdanews.pl @@ -0,0 +1,196 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +my @newsgroups = ( + 1, 'comp.sys.sgi', + 2, 'comp.sys.sgi.graphics', + 3, 'comp.sys.sgi.hardware', + 4, 'sgi.bad-attitude', + 5, 'sgi.engr.all', +); + +use vars qw( $total $news_regex %news_hash @news_count @news_last ); +my ($nnrpd_count, $rn_count, $trn_count, $xrn_count, $vn_count) = (0,0,0,0,0); +my $news_file = pmda_config('PCP_PMDAS_DIR') . '/news/active'; +my $news_indom = 0; +my $pmda; + +sub news_fetch # called once per ``fetch'' pdu, before callbacks +{ + my ( $group, $cmd ); + + $total = 0; + seek ACTIVE,0,0; + while () { + next unless /$news_regex/; + $group = $news_hash{$1} - 1; + $news_last[$group] = $2; + $total += $news_count[$group] = $2 - $3; + } + + ($nnrpd_count, $rn_count, $trn_count, $xrn_count, $vn_count) = (0,0,0,0,0); + if (open(READERS, 'ps -ef |')) { + while () { + s/\b(:?\d\d:){2}\d\d\b/ Mmm DD /; # replace times with dates + s/\s*?(\S+?\s+?){8}//; # nuke the first eight fields + next unless /(\S+)\s*?/; # prepare $cmd with command name + $cmd = $1; + ($cmd eq 'in.nnrpd' || $cmd =~ /\/in\.nnrpd$/) && $nnrpd_count++; + ($cmd eq 'rn' || $cmd =~ /\/rn$/) && $rn_count++; + ($cmd eq 'trn' || $cmd =~ /\/trn$/) && $trn_count++; + ($cmd eq 'xrn' || $cmd =~ /\/xrn$/) && $xrn_count++; + ($cmd eq 'vn' || $cmd =~ /\/vrn$/) && $vn_count++; + } + close READERS; + } + else { + $pmda->log("Cannot execute 'ps' to count news reader processes"); + } +} + +sub news_fetch_callback # must return array of value,status +{ + my ($cluster, $item, $inst) = @_; + + return (PM_ERR_INST, 0) unless ( $inst == PM_IN_NULL || + ( $cluster == 0 && ($item == 301 || $item == 302) && + $inst > 0 && $inst <= ($#newsgroups+1)/2 ) ); + if ($cluster == 0) { + if ($item == 201) { ($total, 1); } # articles.total + elsif ($item == 301) { ($news_count[$inst-1], 1); } # articles.count + elsif ($item == 302) { ($news_last[$inst-1], 1); } # articles.last + elsif ($item == 101) { ($nnrpd_count, 1); } # readers.nnrpd + elsif ($item == 111) { ($rn_count, 1); } # readers.rn + elsif ($item == 112) { ($trn_count, 1); } # readers.trn + elsif ($item == 113) { ($xrn_count, 1); } # readers.xrn + elsif ($item == 114) { ($vn_count, 1); } # readers.vn + else { (PM_ERR_PMID, 0); } + } + else { (PM_ERR_PMID, 0); } +} + +sub news_init +{ + ($#newsgroups > 0 && $#newsgroups % 2 != 0) + || die "Invalid newsgroups array has been specified\n"; + open(ACTIVE, $news_file) || die "Can't open $news_file: $!\n"; + + # build regex using the given newsgroup names + $_ = "^($newsgroups[1]"; + $news_hash{$newsgroups[1]} = $newsgroups[0]; + for (my $i = 2; $i < $#newsgroups; $i += 2) { + $_ .= "|$newsgroups[$i+1]"; + $news_hash{$newsgroups[$i+1]} = $newsgroups[$i]; + } + $news_regex = $_ . ") (\\d+) (\\d+) y\$"; +} + +$pmda = PCP::PMDA->new('news', 28); + +$pmda->add_metric(pmda_pmid(0,201), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.articles.total', + 'Total number of articles received for each newsgroup', +'Total number of articles received for each newsgroup. +Note this is the historical running total, see news.articles.count for +the current total of un-expired articles by newsgroup.'); + +$pmda->add_metric(pmda_pmid(0,301), PM_TYPE_U32, $news_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.articles.count', + 'Total number of un-expired articles in each newsgroup', ''); + +$pmda->add_metric(pmda_pmid(0,302), PM_TYPE_U32, $news_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.articles.last', '', ''); +$pmda->add_metric(pmda_pmid(0,101), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.readers.nnrpd', '', ''); +$pmda->add_metric(pmda_pmid(0,111), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.readers.rn', '', ''); +$pmda->add_metric(pmda_pmid(0,112), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.readers.trn', '', ''); +$pmda->add_metric(pmda_pmid(0,113), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.readers.xrn', '', ''); +$pmda->add_metric(pmda_pmid(0,114), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'news.readers.vn', '', ''); + +$pmda->add_indom($news_indom, \@newsgroups, '', ''); + +$pmda->set_fetch(\&news_fetch); +$pmda->set_fetch_callback(\&news_fetch_callback); + +&news_init; + +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdanews - sample Usenet news performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is an example Performance Metrics Domain Agent (PMDA) which +exports metric values related to a set of newsgroups. + +=head1 INSTALLATION + +If you want access to the names and values for the news performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/news + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/news + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/news/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/news/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/news.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/nfsclient/GNUmakefile b/src/pmdas/nfsclient/GNUmakefile new file mode 100644 index 0000000..cb149f9 --- /dev/null +++ b/src/pmdas/nfsclient/GNUmakefile @@ -0,0 +1,49 @@ +# +# Copyright (c) 2011 SGI. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = nfsclient + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain : ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/nfsclient/Install b/src/pmdas/nfsclient/Install new file mode 100755 index 0000000..3e3677d --- /dev/null +++ b/src/pmdas/nfsclient/Install @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (c) 2011 SGI. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=nfsclient +pmda_interface=2 +dso_opt=false +daemon_opt=false +perl_opt=true +socket_opt=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/nfsclient/Remove b/src/pmdas/nfsclient/Remove new file mode 100755 index 0000000..4a26c90 --- /dev/null +++ b/src/pmdas/nfsclient/Remove @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Copyright (c) 2011 SGI. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=nfsclient + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/nfsclient/pmdanfsclient.pl b/src/pmdas/nfsclient/pmdanfsclient.pl new file mode 100644 index 0000000..380b1cd --- /dev/null +++ b/src/pmdas/nfsclient/pmdanfsclient.pl @@ -0,0 +1,1181 @@ +# +# Copyright (c) 2011 SGI. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +use vars qw( $pmda @all_ops @common_ops @v3only_ops @v4_ops @v41_ops); + +# The instance domain is 0 for all nfs client stats +our $nfsclient_indom = 0; + +# mountstats or other file to use +our $MOUNTSTATS_PATH = "/proc/self/mountstats"; + +# Configuration files for overriding the location of mountstats, mostly for testing purposes +for my $file (pmda_config('PCP_PMDAS_DIR') . '/nfsclient/nfsclient.conf', 'nfsclient.conf') { + eval `cat $file` unless ! -f $file; +} + +# Check env variable for mountstats file to use +if ( defined $ENV{"NFSCLIENT_MOUNTSTATS_PATH"} ) { + $MOUNTSTATS_PATH = $ENV{"NFSCLIENT_MOUNTSTATS_PATH"} +} + +our $noval = PM_ERR_APPVERSION; + +# The stats hash is keyed on the 'mount' string (the mountpoint string), e.g. +# '/home'. The value is a hash ref, and that hash contains all of +# the stats data keyed on pmid_name +our %h = (); + +# +# Parse /proc/self/mountstats and store the stats, taking one pass through +# the file. +# +sub nfsclient_parse_proc_mountstats { + # mountstats output has a section for each mounted filesystems on the + # system. Each section starts with a line like: 'device X mounted on + # Y with fstype Z'. Some filesystems (like nfs) print additional + # output that we need to capture. + open STATS, '<', $MOUNTSTATS_PATH || + ( $pmda->err("pmdanfsclient failed to open $MOUNTSTATS_PATH: $!") && + die "Can't open $MOUNTSTATS_PATH: $!\n") ; + + my $export; + my $nfsinst; + while () { + my $line = $_; + + # does this line represent a mount? + if ($line =~ /^device (\S*) mounted on (\S*) with fstype.*/) { + $export = $1; + my $mtpt = $2; + $nfsinst = $mtpt; + + # is it an nfs mount? + if ($line =~ /.*nfs(4)? statvers=.*/) { + $h{$nfsinst} = {}; # {} is an anonymous hash ref + $h{$nfsinst}->{'nfsclient.export'} = $export; + $h{$nfsinst}->{'nfsclient.mountpoint'} = $mtpt; + } else { + # the following lines aren't nfs so undef + # $nfsinst so that they are ignored below + undef $nfsinst; + next; + } + } + + # skip lines that do not belong to nfs mounts + if (not defined $nfsinst) { + next; + } + + # opts + if ($line =~ /^\topts:\t(.*)$/) { + + # Need to differentiate between options that are not available based on nfs vers, vs options that are not set and therefore default values ? + # Prepopulate based on this constraint + # Try to stay away from the "no" names + # Guess reasonable defaults based on mapping below + # + $h{$nfsinst}->{'nfsclient.options.readmode'} = "rw"; + $h{$nfsinst}->{'nfsclient.options.sync'} = 0; + $h{$nfsinst}->{'nfsclient.options.atime'} = 1; + $h{$nfsinst}->{'nfsclient.options.diratime'} = 1; + $h{$nfsinst}->{'nfsclient.options.vers'} = "3"; + $h{$nfsinst}->{'nfsclient.options.rsize'} = 0; + $h{$nfsinst}->{'nfsclient.options.wsize'} = 0; + $h{$nfsinst}->{'nfsclient.options.bsize'} = 0; + $h{$nfsinst}->{'nfsclient.options.namlen'} = 0; + $h{$nfsinst}->{'nfsclient.options.acregmin'} = 0; + $h{$nfsinst}->{'nfsclient.options.acregmax'} = 0; + $h{$nfsinst}->{'nfsclient.options.acdirmin'} = 0; + $h{$nfsinst}->{'nfsclient.options.acdirmax'} = 0; + $h{$nfsinst}->{'nfsclient.options.recovery'} = "hard"; + $h{$nfsinst}->{'nfsclient.options.posix'} = 0; + $h{$nfsinst}->{'nfsclient.options.cto'} = 1; + $h{$nfsinst}->{'nfsclient.options.ac'} = 1; + $h{$nfsinst}->{'nfsclient.options.lock'} = 1; + $h{$nfsinst}->{'nfsclient.options.acl'} = 1; + $h{$nfsinst}->{'nfsclient.options.rdirplus'} = 1; + $h{$nfsinst}->{'nfsclient.options.sharecache'} = 1; + $h{$nfsinst}->{'nfsclient.options.resvport'} = 1; + $h{$nfsinst}->{'nfsclient.options.proto'} = "tcp"; + $h{$nfsinst}->{'nfsclient.options.port'} = 0; + $h{$nfsinst}->{'nfsclient.options.timeo'} = 600; + $h{$nfsinst}->{'nfsclient.options.retrans'} = 3; + $h{$nfsinst}->{'nfsclient.options.sec'} = "sys"; + + # NFS 3 only options but can be NULL also + $h{$nfsinst}->{'nfsclient.options.mountaddr'} = "unspecified"; + $h{$nfsinst}->{'nfsclient.options.mountvers'} = 0; + $h{$nfsinst}->{'nfsclient.options.mountport'} = 0; + $h{$nfsinst}->{'nfsclient.options.mountproto'} = ""; + + # NFS 4 only + $h{$nfsinst}->{'nfsclient.options.clientaddr'} = ""; + + # All + $h{$nfsinst}->{'nfsclient.options.fsc'} = 0; + $h{$nfsinst}->{'nfsclient.options.migration'} = 0; + $h{$nfsinst}->{'nfsclient.options.lookupcache'} = ""; + $h{$nfsinst}->{'nfsclient.options.local_lock'} = "none"; + + $h{$nfsinst}->{'nfsclient.options.string'} = $1; # Keep this??? + + my @mountopts = split(',', $1); + + foreach my $mountopt (@mountopts) { + chomp $mountopt; + # Is there a better way to do this ?? + # Started with a regex for the full line but got ugly + # + if( $mountopt =~ /^(ro|rw)$/ ){ + $h{$nfsinst}->{'nfsclient.options.readmode'} = "$1"; + } elsif ( $mountopt =~ /^(sync)$/ ){ + $h{$nfsinst}->{'nfsclient.options.sync'} = 1; + } elsif ( $mountopt =~ /^(noatime)$/ ){ + $h{$nfsinst}->{'nfsclient.options.atime'} = 0; + } elsif ( $mountopt =~ /^(nodirtime)$/ ){ + $h{$nfsinst}->{'nfsclient.options.diratime'} = 0; + } elsif ( $mountopt =~ /^vers=([2-4](\.[0-9])?)/ ){ + $h{$nfsinst}->{'nfsclient.options.vers'} = "$1"; + } elsif ( $mountopt =~ /^rsize=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.rsize'} = $1; + } elsif ( $mountopt =~ /^wsize=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.wsize'} = $1; + } elsif ( $mountopt =~ /^bsize=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.bsize'} = $1; + } elsif ( $mountopt =~ /^namlen=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.namlen'} = $1; + } elsif ( $mountopt =~ /^acregmin=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.acregmin'} = $1; + } elsif ( $mountopt =~ /^acregmax=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.acregmax'} = $1; + } elsif ( $mountopt =~ /^acdirmin=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.acdirmin'} = $1; + } elsif ( $mountopt =~ /^acdirmax=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.acdirmax'} = $1; + } elsif ( $mountopt =~ /^(soft|hard)$/ ){ + $h{$nfsinst}->{'nfsclient.options.recovery'} = "$1"; + } elsif ( $mountopt =~ /^(posix)$/ ){ + $h{$nfsinst}->{'nfsclient.options.posix'} = 1; + } elsif ( $mountopt =~ /^(nocto)$/ ){ + $h{$nfsinst}->{'nfsclient.options.cto'} = 0; + } elsif ( $mountopt =~ /^(noac)$/ ){ + $h{$nfsinst}->{'nfsclient.options.ac'} = 0; + } elsif ( $mountopt =~ /^(nolock)$/ ){ + $h{$nfsinst}->{'nfsclient.options.lock'} = 0; + } elsif ( $mountopt =~ /^(noacl)$/ ){ + $h{$nfsinst}->{'nfsclient.options.acl'} = 0; + } elsif ( $mountopt =~ /^(nordirplus)$/ ){ + $h{$nfsinst}->{'nfsclient.options.rdirplus'} = 0; + } elsif ( $mountopt =~ /^(nosharecache)$/ ){ + $h{$nfsinst}->{'nfsclient.options.sharecache'} = 0; + } elsif ( $mountopt =~ /^(noresvport)$/ ){ + $h{$nfsinst}->{'nfsclient.options.resvport'} = 0; + } elsif ( $mountopt =~ /^proto=(tcp|udp|rdma)$/ ){ + $h{$nfsinst}->{'nfsclient.options.proto'} = "$1"; + } elsif ( $mountopt =~ /^port=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.port'} = $1; + } elsif ( $mountopt =~ /^timeo=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.timeo'} = $1; + } elsif ( $mountopt =~ /^retrans=(\d+)$/ ){ + $h{$nfsinst}->{'nfsclient.options.retrans'} = $1; + } elsif ( $mountopt =~ /^sec=(null|sys|krb5|krb5i|krb5p|lkey|lkeyi|lkeyp|spkm|spkmi|spkmp|unknown)$/ ){ + $h{$nfsinst}->{'nfsclient.options.sec'} = "$1"; + } elsif ( $mountopt =~ /^mountaddr=(.*)$/ ){ # NFS3 only + $h{$nfsinst}->{'nfsclient.options.mountaddr'} = "$1"; + } elsif ( $mountopt =~ /^mountvers=(\d+)$/ ){ # NFS3 only + $h{$nfsinst}->{'nfsclient.options.mountvers'} = $1; + } elsif ( $mountopt =~ /^mountport=(\d+)$/ ){ # NFS3 only + $h{$nfsinst}->{'nfsclient.options.mountport'} = $1; + } elsif ( $mountopt =~ /^mountproto=(udp|tcp|auto|udp6|tcp6)$/ ){ # NFS3 only + $h{$nfsinst}->{'nfsclient.options.mountproto'} = "$1" + } elsif ( $mountopt =~ /^clientaddr=(.*)$/ ){ # NFS4 only + $h{$nfsinst}->{'nfsclient.options.clientaddr'} = "$1"; + } elsif ( $mountopt =~ /^(fsc)$/ ){ + $h{$nfsinst}->{'nfsclient.options.fsc'} = 1; + } elsif ( $mountopt =~ /^(migration)$/ ){ + $h{$nfsinst}->{'nfsclient.options.migration'} = 1; + } elsif ( $mountopt =~ /^lookupcache=(none|pos)$/ ){ + $h{$nfsinst}->{'nfsclient.options.lookupcache'} = "$1"; + } elsif ( $mountopt =~ /^local_lock=(none|all|flock|posix)$/ ){ + $h{$nfsinst}->{'nfsclient.options.local_lock'} = "$1"; + } + } + } + #age + if ($line =~ /^\tage:\t(.*)$/) { + $h{$nfsinst}->{'nfsclient.age'} = $1; + } + #caps + if ($line =~ /^\tcaps:\t(.*)$/) { + $h{$nfsinst}->{'nfsclient.capabilities'} = $1; + } + #sec + if ($line =~ /^\tsec:\t(.*)$/) { + $h{$nfsinst}->{'nfsclient.security'} = $1; + } + # events + if ($line =~ /^\tevents:\t(.*)$/) { + ($h{$nfsinst}->{'nfsclient.events.inoderevalidate'}, + $h{$nfsinst}->{'nfsclient.events.dentryrevalidate'}, + $h{$nfsinst}->{'nfsclient.events.datainvalidate'}, + $h{$nfsinst}->{'nfsclient.events.attrinvalidate'}, + $h{$nfsinst}->{'nfsclient.events.vfsopen'}, + $h{$nfsinst}->{'nfsclient.events.vfslookup'}, + $h{$nfsinst}->{'nfsclient.events.vfsaccess'}, + $h{$nfsinst}->{'nfsclient.events.vfsupdatepage'}, + $h{$nfsinst}->{'nfsclient.events.vfsreadpage'}, + $h{$nfsinst}->{'nfsclient.events.vfsreadpages'}, + $h{$nfsinst}->{'nfsclient.events.vfswritepage'}, + $h{$nfsinst}->{'nfsclient.events.vfswritepages'}, + $h{$nfsinst}->{'nfsclient.events.vfsgetdents'}, + $h{$nfsinst}->{'nfsclient.events.vfssetattr'}, + $h{$nfsinst}->{'nfsclient.events.vfsflush'}, + $h{$nfsinst}->{'nfsclient.events.vfsfsync'}, + $h{$nfsinst}->{'nfsclient.events.vfslock'}, + $h{$nfsinst}->{'nfsclient.events.vfsrelease'}, + $h{$nfsinst}->{'nfsclient.events.congestionwait'}, + $h{$nfsinst}->{'nfsclient.events.setattrtrunc'}, + $h{$nfsinst}->{'nfsclient.events.extendwrite'}, + $h{$nfsinst}->{'nfsclient.events.sillyrename'}, + $h{$nfsinst}->{'nfsclient.events.shortread'}, + $h{$nfsinst}->{'nfsclient.events.shortwrite'}, + $h{$nfsinst}->{'nfsclient.events.delay'}) = + split(/ /, $1); + } + + # bytes + if ($line =~ /\tbytes:\t(.*)$/) { + ($h{$nfsinst}->{'nfsclient.bytes.read.normal'}, + $h{$nfsinst}->{'nfsclient.bytes.write.normal'}, + $h{$nfsinst}->{'nfsclient.bytes.read.direct'}, + $h{$nfsinst}->{'nfsclient.bytes.write.direct'}, + $h{$nfsinst}->{'nfsclient.bytes.read.server'}, + $h{$nfsinst}->{'nfsclient.bytes.write.server'}, + $h{$nfsinst}->{'nfsclient.pages.read'}, + $h{$nfsinst}->{'nfsclient.pages.write'}) = + split(/ /, $1); + } + + # xprt + if ($line =~ /\txprt:\t(.*)$/) { + my @stats = split(/ /, $1); + my $xprt_prot = shift(@stats); + + if( $xprt_prot eq "tcp"){ + + ($h{$nfsinst}->{'nfsclient.xprt.srcport'}, + $h{$nfsinst}->{'nfsclient.xprt.bind_count'}, + $h{$nfsinst}->{'nfsclient.xprt.connect_count'}, + $h{$nfsinst}->{'nfsclient.xprt.connect_time'}, + $h{$nfsinst}->{'nfsclient.xprt.idle_time'}, + $h{$nfsinst}->{'nfsclient.xprt.sends'}, + $h{$nfsinst}->{'nfsclient.xprt.recvs'}, + $h{$nfsinst}->{'nfsclient.xprt.bad_xids'}, + $h{$nfsinst}->{'nfsclient.xprt.req_u'}, + $h{$nfsinst}->{'nfsclient.xprt.bklog_u'}) = + @stats; + + # Unused RDMA Elements + $h{$nfsinst}->{'nfsclient.xprt.read_chunks'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.write_chunks'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.reply_chunks'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.total_rdma_req'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.total_rdma_rep'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.pullup'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.fixup'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.hardway'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.failed_marshal'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.bad_reply'} = 0; + + } elsif ( $xprt_prot eq "udp"){ + ($h{$nfsinst}->{'nfsclient.xprt.srcport'}, + $h{$nfsinst}->{'nfsclient.xprt.bind_count'}, + $h{$nfsinst}->{'nfsclient.xprt.sends'}, + $h{$nfsinst}->{'nfsclient.xprt.recvs'}, + $h{$nfsinst}->{'nfsclient.xprt.bad_xids'}, + $h{$nfsinst}->{'nfsclient.xprt.req_u'}, + $h{$nfsinst}->{'nfsclient.xprt.bklog_u'}) = + @stats; + + # Unused TCP elements + $h{$nfsinst}->{'nfsclient.xprt.connect_count'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.connect_time'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.idle_time'} = 0; + + # Unused RDMA Elements + $h{$nfsinst}->{'nfsclient.xprt.read_chunks'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.write_chunks'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.reply_chunks'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.total_rdma_req'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.total_rdma_rep'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.pullup'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.fixup'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.hardway'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.failed_marshal'} = 0; + $h{$nfsinst}->{'nfsclient.xprt.bad_reply'} = 0; + + } elsif ( $xprt_prot eq "rdma"){ + ($h{$nfsinst}->{'nfsclient.xprt.srcport'}, + $h{$nfsinst}->{'nfsclient.xprt.bind_count'}, + $h{$nfsinst}->{'nfsclient.xprt.connect_count'}, + $h{$nfsinst}->{'nfsclient.xprt.connect_time'}, + $h{$nfsinst}->{'nfsclient.xprt.idle_time'}, + $h{$nfsinst}->{'nfsclient.xprt.sends'}, + $h{$nfsinst}->{'nfsclient.xprt.recvs'}, + $h{$nfsinst}->{'nfsclient.xprt.bad_xids'}, + $h{$nfsinst}->{'nfsclient.xprt.req_u'}, + $h{$nfsinst}->{'nfsclient.xprt.bklog_u'}, + $h{$nfsinst}->{'nfsclient.xprt.read_chunks'}, + $h{$nfsinst}->{'nfsclient.xprt.write_chunks'}, + $h{$nfsinst}->{'nfsclient.xprt.reply_chunks'}, + $h{$nfsinst}->{'nfsclient.xprt.total_rdma_req'}, + $h{$nfsinst}->{'nfsclient.xprt.total_rdma_rep'}, + $h{$nfsinst}->{'nfsclient.xprt.pullup'}, + $h{$nfsinst}->{'nfsclient.xprt.fixup'}, + $h{$nfsinst}->{'nfsclient.xprt.hardway'}, + $h{$nfsinst}->{'nfsclient.xprt.failed_marshal'}, + $h{$nfsinst}->{'nfsclient.xprt.bad_reply'}) = + @stats; + + } else { + next; + } + } + + # per-op statistics + # pre-populate all possible ops from v3, v4, v4.1 and set to noval + # ops defined below + for my $opname (@all_ops) { + $h{$nfsinst}->{"nfsclient.ops.$opname.ops"} = $noval; + $h{$nfsinst}->{"nfsclient.ops.$opname.ntrans"} = $noval; + $h{$nfsinst}->{"nfsclient.ops.$opname.timeouts"} = $noval; + $h{$nfsinst}->{"nfsclient.ops.$opname.bytes_sent"} = $noval; + $h{$nfsinst}->{"nfsclient.ops.$opname.bytes_recv"} = $noval; + $h{$nfsinst}->{"nfsclient.ops.$opname.queue"} = $noval; + $h{$nfsinst}->{"nfsclient.ops.$opname.rtt"} = $noval; + $h{$nfsinst}->{"nfsclinet.ops.$opname.execute"} = $noval; + } + + if ($line =~ /\tper-op statistics$/) { + # We'll do these a bit differently since they are not + # all on the same line. Just loop until we don't match + # anymore. + # v4 ops can have underscore + while ( =~ + /^\s*([A-Z_]*): (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*)$/) { + my $op_name = "\L$1"; + ($h{$nfsinst}->{"nfsclient.ops.$op_name.ops"}, + $h{$nfsinst}->{"nfsclient.ops.$op_name.ntrans"}, + $h{$nfsinst}->{"nfsclient.ops.$op_name.timeouts"}, + $h{$nfsinst}->{"nfsclient.ops.$op_name.bytes_sent"}, + $h{$nfsinst}->{"nfsclient.ops.$op_name.bytes_recv"}, + $h{$nfsinst}->{"nfsclient.ops.$op_name.queue"}, + $h{$nfsinst}->{"nfsclient.ops.$op_name.rtt"}, + $h{$nfsinst}->{"nfsclinet.ops.$op_name.execute"}) = + ($2, $3, $4, $5, $6, $7, $8, $9); + } + } + } + + close STATS; +} + +# +# fetch is called once by pcp for each refresh and then the fetch callback is +# called to query each statistic individually +# +sub nfsclient_fetch { + nfsclient_parse_proc_mountstats(); + + our $pmda->replace_indom($nfsclient_indom, \%h); +} + +sub nfsclient_fetch_callback { + my ($cluster, $item, $inst) = @_; + + my $lookup = pmda_inst_lookup($nfsclient_indom, $inst); + return (PM_ERR_INST, 0) unless defined($lookup); + + my $pmid_name = pmda_pmid_name($cluster, $item) + or die "Unknown metric name: cluster $cluster item $item\n"; + + return ($lookup->{$pmid_name}, 1); + +} + +# the PCP::PMDA->new line is parsed by the check_domain rule of the PMDA build +# process, so there are special requirements: no comments, the domain has to +# be a bare number. +# +our $pmda = PCP::PMDA->new('nfsclient', 62); + +# metrics go here, with full descriptions + +# general - cluster 0 +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.export', + 'Export', + ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.mountpoint', + 'Mount Point', + ''); + +# opts - cluster 1 +# +# Options in order: +# NULL is not present +# from super.c in nfs_show_mount_options +# +# ro | rw +# sync | NULL +# noatime | NULL +# nodiratime | NULL +# vers=([2-4](\.[0-9])?) +# rsize=%u +# wsize=%u +# bsize=%u | NULL +# namlen=%u +# acregmin=%u | NULL +# acregmax=%u | NULL +# acdirmin=%u | NULL +# acdirmax=%u | NULL +# soft | hard +# posix | NULL +# nocto | NULL +# noac | NULL +# nolock | NULL +# noacl | NULL +# nordirplus | NULL +# nosharecache | NULL +# noresvport | NULL +# proto=(tcp|udp|rdma) +# port=%u | NULL +# timeo=%lu +# retrans=%u +# sec=(null|sys|krb5|krb5i|krb5p|lkey|lkeyi|lkeyp|spkm|spkmi|spkmp|unknown) # null and unknown are those exact literal strings + +# NFS 3 only +# Below block may not exist at all if NFS_MOUNT_LEGACY_INTERFACE +# mountaddr="ip4addr|ip6addr|unspecified" +# mountvers=%u | NULL +# mountport=%u | NULL +# mountproto=(udp|tcp|auto|udp6|tcp6|NULL) # NULL if showdefaults is false + +# NFS 4 Only +# clientaddr=(ip4address|ip6address) + +# fsc | NULL +# migration | NULL +# lookupcache=(none|pos) | NULL +# local_lock=(none|all|flock|posix) + +# TODO +# deprecated after 2.6.25, do we care about these ? +# intr|nointr + +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.string', + 'Full Options String', + ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.readmode', + 'rw or ro mount mode', + ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.sync', + 'boolean sync mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,4), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.atime', + 'boolean atime mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,5), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.diratime', + 'boolean diratime mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,6), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.vers', + 'nfs version', + ''); +$pmda->add_metric(pmda_pmid(1,7), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.rsize', + 'rsize mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,8), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.wsize', + 'wsize mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,9), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.bsize', + 'bsize mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,10), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.namlen', + 'namlen mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,11), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.acregmin', + 'acregmin mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,12), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.acregmax', + 'acregmax mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,13), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.acdirmin', + 'acdirmin mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,14), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.acdirmax', + 'acdirmax mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,15), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.recovery', + 'hard or soft recovery behavior', + ''); +$pmda->add_metric(pmda_pmid(1,16), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.posix', + 'boolean posix mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,17), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.cto', + 'boolean cto mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,18), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.ac', + 'boolean ac mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,19), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.lock', + 'boolean lock mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,20), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.acl', + 'boolean acl mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,21), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.rdirplus', + 'boolean rdirplus mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,22), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.sharecache', + 'boolean sharecache mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,23), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.resvport', + 'boolean resvport mount option used', + ''); +$pmda->add_metric(pmda_pmid(1,24), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.proto', + 'nfs protocol: udp|tcp|rdma', + ''); +$pmda->add_metric(pmda_pmid(1,25), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.port', + 'port mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,26), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.timeo', + 'timeo mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,27), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.retrans', + 'retrans mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,28), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.sec', + 'sec mount parameter', + ''); +# NFS3 only +$pmda->add_metric(pmda_pmid(1,29), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.mountaddr', + 'mountaddr mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,30), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.mountvers', + 'mountvers mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,31), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.mountport', + 'mountport mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,32), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.mountproto', + 'mountproto mount parameter', + ''); +# NFS4 only +$pmda->add_metric(pmda_pmid(1,33), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.clientaddr', + 'clientaddr mount parameter', + ''); +# All +$pmda->add_metric(pmda_pmid(1,34), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.fsc', + 'fsc mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,35), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.migration', + 'migration mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,36), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.lookupcache', + 'lookupcache mount parameter', + ''); +$pmda->add_metric(pmda_pmid(1,37), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.options.local_lock', + 'local_lock mount parameter', + ''); + +# age - cluster 2 +$pmda->add_metric(pmda_pmid(2,1), pmda_ulong, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'nfsclient.age', + 'Age in Seconds', + ''); +# caps - cluster 3 - Any use in parsing this ? +$pmda->add_metric(pmda_pmid(3,1), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.capabilities', + 'Capabilities', + ''); + +# sec - cluster 4 - Any use in parsing this ? +$pmda->add_metric(pmda_pmid(4,1), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.security', + 'Security Flavor', + ''); + +# events - cluster 5 +$pmda->add_metric(pmda_pmid(5,1), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.inoderevalidate', + 'NFSIOS_INODEREVALIDATE', +'incremented in __nfs_revalidate_inode whenever an inode is revalidated'); + +$pmda->add_metric(pmda_pmid(5,2), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.dentryrevalidate', + 'NFSIOS_DENTRYREVALIDATE', +'incremented in nfs_lookup_revalidate whenever a dentry is revalidated'); + +$pmda->add_metric(pmda_pmid(5,3), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.datainvalidate', + 'NFSIOS_DATAINVALIDATE', +'incremented in nfs_invalidate_mapping_nolock when data cache for an inode ' . +'is invalidated'); + +$pmda->add_metric(pmda_pmid(5,4), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.attrinvalidate', + 'NFSIOS_ATTRINVALIDATE', +'incremented in nfs_zap_caches_locked and nfs_update_inode when an the ' . +'attribute cache for an inode has been invalidated'); + +$pmda->add_metric(pmda_pmid(5,5), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsopen', + 'NFSIOS_VFSOPEN', +'incremented in nfs_file_open and nfs_opendir whenever a file or directory ' . +'is opened'); + +$pmda->add_metric(pmda_pmid(5,6), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfslookup', + 'NFSIOS_VFSLOOKUP', +'incremented in nfs_lookup on every lookup'); + +$pmda->add_metric(pmda_pmid(5,7), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsaccess', + '', ''); + +$pmda->add_metric(pmda_pmid(5,8), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsupdatepage', + '', ''); + +$pmda->add_metric(pmda_pmid(5,9), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsreadpage', + '', ''); + +$pmda->add_metric(pmda_pmid(5,10), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsreadpages', + '', ''); + +$pmda->add_metric(pmda_pmid(5,11), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfswritepage', + '', ''); + +$pmda->add_metric(pmda_pmid(5,12), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfswritepages', + '', ''); + +$pmda->add_metric(pmda_pmid(5,13), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsgetdents', + '', ''); + +$pmda->add_metric(pmda_pmid(5,14), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfssetattr', + '', ''); + +$pmda->add_metric(pmda_pmid(5,15), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsflush', + '', ''); + +$pmda->add_metric(pmda_pmid(5,16), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsfsync', + '', ''); + +$pmda->add_metric(pmda_pmid(5,17), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfslock', + '', ''); + +$pmda->add_metric(pmda_pmid(5,18), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.vfsrelease', + '', ''); + +$pmda->add_metric(pmda_pmid(5,19), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.congestionwait', + '', ''); + +$pmda->add_metric(pmda_pmid(5,20), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.setattrtrunc', + '', ''); + +$pmda->add_metric(pmda_pmid(5,21), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.extendwrite', + '', ''); + +$pmda->add_metric(pmda_pmid(5,22), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.sillyrename', + '', ''); + +$pmda->add_metric(pmda_pmid(5,23), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.shortread', + '', ''); + +$pmda->add_metric(pmda_pmid(5,24), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.shortwrite', + '', ''); + +$pmda->add_metric(pmda_pmid(5,25), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.events.delay', + '', ''); + +# bytes - cluster 6 +$pmda->add_metric(pmda_pmid(6,1), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'nfsclient.bytes.read.normal', + 'NFSIOS_NORMALREADBYTES', +'the number of bytes read by applications via the read system call interface'); + +$pmda->add_metric(pmda_pmid(6,2), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'nfsclient.bytes.write.normal', + 'NFSIOS_NORMALWRITTENBYTES', +'the number of bytes written by applications via the write system call ' . +'interface'); + +$pmda->add_metric(pmda_pmid(6,3), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'nfsclient.bytes.read.direct', + 'NFSIOS_DIRECTREADBYTES', +'the number of bytes read by applications from files opened with the ' . +'O_DIRECT flag'); + +$pmda->add_metric(pmda_pmid(6,4), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'nfsclient.bytes.write.direct', + 'NFSIOS_DIRECTWRITTENBYTES', +'the number of bytes written by applications to files opened with the ' . +'O_DIRECT flag'); + +$pmda->add_metric(pmda_pmid(6,5), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'nfsclient.bytes.read.server', + 'NFSIOS_SERVERREADBYTES', +'the number of bytes read from the nfs server by the nfs client via nfs ' . +'read requests'); + +$pmda->add_metric(pmda_pmid(6,6), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'nfsclient.bytes.write.server', + 'NFSIOS_SERVERWRITTENBYTES', +'the number of bytes written to the nfs server by the nfs client via nfs ' . +'write requests'); + +$pmda->add_metric(pmda_pmid(6,7), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.pages.read', + 'NFSIOS_READPAGES', +'the number of pages read via nfs_readpage or nfs_readpages'); + +$pmda->add_metric(pmda_pmid(6,8), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.pages.write', + 'NFSIOS_WRITEPAGES', +'the number of pages written via nfs_writepage or nfs_writepages'); + +# xprt - cluster 7 +# +# We have three possible transports: udp, tcp, rdma. +# Fill in 0 for ones that dont apply. Types for RDMA from net/sunrpc/xprtrdma/transport.c : xprt_rdma_print_stats +# +$pmda->add_metric(pmda_pmid(7,1), PM_TYPE_STRING, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nfsclient.xprt.srcport', + 'tcp source port', +'source port on the client'); + +$pmda->add_metric(pmda_pmid(7,2), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.bind_count', + 'count of rpcbind get_port calls', +'incremented in rpcb_getport_async: \"obtain the port for a given RPC ' . +'service on a given host\"'); + +$pmda->add_metric(pmda_pmid(7,3), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.connect_count', + 'count of tcp connects', +'incremented in xs_tcp_finish_connecting and xprt_connect_status. This is ' . +'a count of the number of tcp (re)connects (only of which is active at a ' . +'time) for this mount.'); + +$pmda->add_metric(pmda_pmid(7,4), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.connect_time', + 'jiffies waiting for connect', +'Summed in xprt_connect_status, it is stored and printed in jiffies. This ' . +'the sum of all connection attempts: how long was spent waiting to connect.'); + +$pmda->add_metric(pmda_pmid(7,5), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'nfsclient.xprt.idle_time', + 'transport idle time', +'How long has it been since the transport has been used. Stored and ' . +'calculated in jiffies and printed in seconds.'); + +$pmda->add_metric(pmda_pmid(7,6), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.sends', + 'count of tcp transmits', +'Incremented in xprt_transmit upon transmit completion of each rpc.'); + +$pmda->add_metric(pmda_pmid(7,7), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.recvs', + 'count of tcp receives', +'Incremented in xprt_complete_rqst when reply processing is complete.'); + +$pmda->add_metric(pmda_pmid(7,8), PM_TYPE_U32, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.bad_xids', + 'count of bad transaction identifiers', +'When processing an rpc reply it is necessary to look up the original ' . +'rpc_rqst using xprt_lookup_rqst. If the rpc_rqst that prompted the reply ' . +'cannot be found on the transport bad_xids is incremented.'); + +$pmda->add_metric(pmda_pmid(7,9), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.req_u', + 'average requests on the wire', +'FIXME: The comment in struct stat says: \"average requests on the wire\", ' . +'but the actual calculation in xprt_transmit is: ' . +'xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs;\n' . +'This stat may be broken.'); + +$pmda->add_metric(pmda_pmid(7,10), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.backlog_u', + 'backlog queue utilization', +'FIXME: here is the calculation in xprt_transmit: ' . +'xprt->stat.bklog_u += xprt->backlog.qlen;\n ' . +'qlen is incremented in __rpc_add_wait_queue and decremented in ' . +'__rpc_remove_wait_queue.'); + +$pmda->add_metric(pmda_pmid(7,11), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.read_chunks', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,12), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.write_chunks', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,13), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.reply_chunks', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,14), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.total_rdma_req', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,15), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.total_rdma_rep', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,16), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.pullup', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,17), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.fixup', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,18), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.hardway', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,19), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.failed_marshal', + '', + ''); + +$pmda->add_metric(pmda_pmid(7,20), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nfsclient.xprt.bad_reply', + '', + ''); + +# ops - cluster 8 +# +# Different versions have different ops. We just List them all and fill in +# only what is appropriate for this version. Non matching ones will return noval +# from above. + +# ops available in v3, v4 and v4.1 +# +our @common_ops = ('null', 'getattr', 'setattr', 'lookup', 'access', 'readlink', + 'read', 'write', 'create', 'symlink', 'remove', + 'rename', 'link', 'readdir', + 'fsinfo', 'pathconf', 'commit'); + +# ops only in v3 +# +our @v3only_ops = ('fsstat', 'mkdir', 'mknod', 'readdirplus', 'rmdir'); + +# ops ADDED in v4, some v3 ops are invalid : fsstat, mkdir, mknod, readdirplus, rmdir +# +our @v4_ops = ('close', 'create_session', 'delegreturn', 'destroy_session', 'exchange_id', + 'fs_locations', 'getacl', 'getdeviceinfo', 'get_lease_time', 'layoutcommit', + 'layoutget', 'layoutreturn', 'lock', 'lockt', 'locku', 'lookup_root', + 'open', 'open_confirm', 'open_downgrade', 'open_noattr', 'reclaim_complete', + 'release_lockowner', 'renew', 'secinfo', 'sequence', 'server_caps', 'setacl', + 'setclientid', 'setclientid_confirm', 'statfs'); + +# ops ADDED in v4.1, same v3 ops removed as in v4, all v4 ops should exist +# +our @v41_ops = ('bind_conn_to_session', 'destroy_clientid', 'free_stateid', 'getdevicelist', + 'secinfo_no_name', 'test_stateid'); + +our @all_ops = ( @common_ops, @v3only_ops, @v4_ops, @v41_ops ); + +my $item = 1; +for my $op_name (@all_ops) { + $pmda->add_metric(pmda_pmid(8, $item++), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "nfsclient.ops.$op_name.ops", + 'count of operations', +'rpc count for this op, only bumped in rpc_count_iostats'); + + $pmda->add_metric(pmda_pmid(8, $item++), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "nfsclient.ops.$op_name.ntrans", + 'count of transmissions', +'there can be more than one transmission per rpc'); + + $pmda->add_metric(pmda_pmid(8, $item++), pmda_ulong, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "nfsclient.ops.$op_name.timeouts", + 'count of major timeouts', +'XXX fill me in'); + + $pmda->add_metric(pmda_pmid(8, $item++), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + "nfsclient.ops.$op_name.bytes_sent", + 'count of bytes out', +'How many bytes are sent for this procedure type. This indicates how much ' . +'load this procedure is putting on the network. It includes the RPC and ULP' . +'headers, and the request payload'); + + $pmda->add_metric(pmda_pmid(8, $item++), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + "nfsclient.ops.$op_name.bytes_recv", + 'count of bytes in', +'How many bytes are received for this procedure type. This indicates how ' . +'much load this procedure is putting on the network. It includes RPC and ' . +'ULP headers, and the request payload'); + + $pmda->add_metric(pmda_pmid(8, $item++), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + "nfsclient.ops.$op_name.queue", + 'milliseconds queued for transmit', +'The length of time an RPC request waits in queue before transmission in ' . +' milliseconds.'); + + $pmda->add_metric(pmda_pmid(8, $item++), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + "nfsclient.ops.$op_name.rtt", + 'milliseconds for rpc round trip time', +'The network + server latency of the request in milliseconds.'); + + $pmda->add_metric(pmda_pmid(8, $item++), PM_TYPE_U64, $nfsclient_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + "nfsclient.ops.$op_name.execute", + 'milliseconds for rpc execution', +'The total time the request spent from init to release in milliseconds.'); +} + +&nfsclient_parse_proc_mountstats; + +$nfsclient_indom = $pmda->add_indom($nfsclient_indom, {}, '', ''); + +$pmda->set_fetch(\&nfsclient_fetch); +$pmda->set_fetch_callback(\&nfsclient_fetch_callback); + +$pmda->run; + +=pod + +=head1 NAME + +pmdanfsclient - nfs client statistics performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from the /proc/self/mountstats interface to provide information +on nfs mounts. + +=head1 INSTALLATION + +If you want access to the names and values for the nfsclient performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/nfsclient + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/nfsclient + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/nfsclient/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/nfsclient/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/nfsclient.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and nfs(5) and mountstats(8). + diff --git a/src/pmdas/nginx/GNUmakefile b/src/pmdas/nginx/GNUmakefile new file mode 100644 index 0000000..ddc5a5a --- /dev/null +++ b/src/pmdas/nginx/GNUmakefile @@ -0,0 +1,55 @@ +#!gmake +# +# Copyright (c) 2013 Ryan Doyle. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = nginx +DOMAIN = NGINX +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl $(IAM).conf + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + $(INSTALL) -m 644 $(IAM).conf $(PMDADIR)/$(IAM).conf + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/nginx/Install b/src/pmdas/nginx/Install new file mode 100755 index 0000000..8ad005c --- /dev/null +++ b/src/pmdas/nginx/Install @@ -0,0 +1,33 @@ +#!/bin/sh +# +# Copyright (c) 2013 Ryan Doyle. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=nginx +perl_opt=true +daemon_opt=false +forced_restart=false + +perl -e "use LWP::UserAgent" 2>/dev/null +if test $? -ne 0 +then + echo "LWP::UserAgent perl module is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/nginx/Remove b/src/pmdas/nginx/Remove new file mode 100755 index 0000000..a5705a3 --- /dev/null +++ b/src/pmdas/nginx/Remove @@ -0,0 +1,23 @@ +#! /bin/sh +# +# Copyright (c) 2013 Ryan Doyle. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=nginx + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/nginx/nginx.conf b/src/pmdas/nginx/nginx.conf new file mode 100644 index 0000000..3a4f664 --- /dev/null +++ b/src/pmdas/nginx/nginx.conf @@ -0,0 +1,2 @@ +#$nginx_status_url = "http://localhost/nginx_status"; +#$nginx_fetch_timeout = 1; diff --git a/src/pmdas/nginx/pmdanginx.pl b/src/pmdas/nginx/pmdanginx.pl new file mode 100755 index 0000000..3ec8b65 --- /dev/null +++ b/src/pmdas/nginx/pmdanginx.pl @@ -0,0 +1,146 @@ +# +# Copyright (c) 2013 Ryan Doyle. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use LWP::UserAgent; + +my @nginx_status = (); +my $nginx_status_url = "http://localhost/nginx_status"; +my $nginx_fetch_timeout = 1; +my $http_client = LWP::UserAgent->new; + +# Configuration files for overriding the above settings +for my $file (pmda_config('PCP_PMDAS_DIR') . '/nginx/nginx.conf', 'nginx.conf') { + eval `cat $file` unless ! -f $file; +} + +$http_client->agent('pmdanginx'); +$http_client->timeout($nginx_fetch_timeout); + +sub update_nginx_status +{ + my $response = $http_client->get($nginx_status_url); + if ($response->is_success) { + # All the content on the status page are digits. Map the array + # index to the metric item ID. + @nginx_status = ($response->decoded_content =~ m/(\d+)/gm); + } else { + @nginx_status = undef; + } +} + +sub nginx_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + if ($cluster == 0 && defined($nginx_status[$item])){ + return ($nginx_status[$item], 1); + } + return (PM_ERR_PMID, 0); +} + +my $pmda = PCP::PMDA->new('nginx', 117); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nginx.active', + 'Number of active connections', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nginx.accepts_count', + 'Total number of accepted connections', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nginx.handled_count', + 'Total number of handled connections', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'nginx.requests_count', + 'Total number of requests', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nginx.reading', + 'Reading the request header', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nginx.writing', + 'Reading the request body, processing the request or writing response', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'nginx.waiting', + 'Keepalive connections', ''); + +$pmda->set_fetch_callback(\&nginx_fetch_callback); +$pmda->set_refresh(\&update_nginx_status); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdanginx - nginx performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from nginx. + + +=head1 INSTALLATION + +This PMDA requires that the nginx stub_status module is active and +available at http://localhost/nginx_status + + +Install the nginx PMDA by using the B script as root: + + # cd $PCP_PMDAS_DIR/nginx + # ./Install + +To uninstall, do the following as root: + + # cd $PCP_PMDAS_DIR/nginx + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/nginx/nginx.conf + +optional configuration file for B + +=item $PCP_PMDAS_DIR/nginx/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/nginx/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/nginx.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/nvidia/GNUmakefile b/src/pmdas/nvidia/GNUmakefile new file mode 100644 index 0000000..622c726 --- /dev/null +++ b/src/pmdas/nvidia/GNUmakefile @@ -0,0 +1,50 @@ +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = nvidia +DOMAIN = NVML + +CMDTARGET = pmdanvidia$(EXECSUFFIX) +LIBTARGET = pmda_nvidia.$(DSOSUFFIX) +CFILES = localnvml.c nvidia.c +HFILES = localnvml.h +DFILES = README +LSRCFILES = Install Remove root help pmns $(DFILES) +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_DLOPEN) +LCFLAGS += -DDSOSUFFIX=\"$(DSOSUFFIX)\" + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LDIRT = domain.h *.log *.dir *.pag so_locations + +default: $(LIBTARGET) $(CMDTARGET) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns domain.h $(PMDADIR) + +nvidia.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/nvidia/Install b/src/pmdas/nvidia/Install new file mode 100755 index 0000000..6fd401e --- /dev/null +++ b/src/pmdas/nvidia/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the trivial PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=nvidia +pmda_interface=2 +dso_opt=true +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/nvidia/README b/src/pmdas/nvidia/README new file mode 100755 index 0000000..114896d --- /dev/null +++ b/src/pmdas/nvidia/README @@ -0,0 +1,7 @@ +Readme +NVIDIA PMDA +=========== + +The NVIDIA PMDA is a PCP module for gathering metrics on the performance of +NVIDIA graphics cards. It uses the NVIDIA Management Library (NVML) to query +the states of attached cards. diff --git a/src/pmdas/nvidia/Remove b/src/pmdas/nvidia/Remove new file mode 100755 index 0000000..5e28c15 --- /dev/null +++ b/src/pmdas/nvidia/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the nvidia PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=nvidia + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/nvidia/help b/src/pmdas/nvidia/help new file mode 100644 index 0000000..38f7b4a --- /dev/null +++ b/src/pmdas/nvidia/help @@ -0,0 +1,72 @@ +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# NVIDIA PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ nvidia.numcards Number of Graphics Cards +The number of NVIDIA Graphics cards installed in this system + +@ nvidia.gpuid GPU ID +Zero indexed id of this NVIDIA card + +@ nvidia.cardname GPU Name +The name of the graphics card + +@ nvidia.busid Card Bus ID +The Bus ID as reported by the NVIDIA tools, not lspci + +@ nvidia.temp The temperature of the card +The Temperature of the GPU on the NVIDIA card in degrees celcius. + +@ nvidia.fanspeed Fanspeed +Speed of the GPU fan as a percentage of the maximum + +@ nvidia.perfstate NVIDIA performance state +The PX performance state as reported from NVML. Value is an integer +which should range from 0 (maximum performance) to 15 (minimum). If +the state is unknown the reported value will be 32, however. + +@ nvidia.gpuactive Percentage of GPU utilization +Percentage of time over the past sample period during which one or more +kernels was executing on the GPU. + +@ nvidia.memactive Percentage of time spent accessing memory +Percent of time over the past sample period during which global (device) +memory was being read or written. This metric shows if the memory is +actively being accesed, and is not correlated to storage amount used. + +@ nvidia.memused Allocated FB memory +Amount of GPU FB memory that has currently been allocated, in bytes. +Note that the driver/GPU always sets aside a small amount of memory +for bookkeeping. + +@ nvidia.memtotal Total FB memory available +The total amount of GPU FB memory available on the card, in bytes. + +@ nvidia.memfree Unallocated FB memory +Amount of GPU FB memory that is not currently allocated, in bytes. diff --git a/src/pmdas/nvidia/localnvml.c b/src/pmdas/nvidia/localnvml.c new file mode 100644 index 0000000..2cadeb9 --- /dev/null +++ b/src/pmdas/nvidia/localnvml.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "pmapi.h" +#include "impl.h" +#if defined(HAVE_DLFCN_H) +#include +#endif +#include "localnvml.h" + +/* + * Implements NVML interfaces based on: + * http://docs.nvidia.com/deploy/nvml-api/index.html + * ... using either a dlopen'd 3rd party or "no values available". + */ + +struct { + const char *symbol; + void *handle; +} nvml_symtab[] = { + { .symbol = "nvmlInit" }, + { .symbol = "nvmlShutdown" }, + { .symbol = "nvmlDeviceGetCount" }, + { .symbol = "nvmlDeviceGetHandleByIndex" }, + { .symbol = "nvmlDeviceGetName" }, + { .symbol = "nvmlDeviceGetPciInfo" }, + { .symbol = "nvmlDeviceGetFanSpeed" }, + { .symbol = "nvmlDeviceGetTemperature" }, + { .symbol = "nvmlDeviceGetUtilizationRates" }, + { .symbol = "nvmlDeviceGetMemoryInfo" }, + { .symbol = "nvmlDeviceGetPerformanceState" }, +}; +enum { + NVML_INIT, + NVML_SHUTDOWN, + NVML_DEVICE_GET_COUNT, + NVML_DEVICE_GET_HANDLEBYINDEX, + NVML_DEVICE_GET_NAME, + NVML_DEVICE_GET_PCIINFO, + NVML_DEVICE_GET_FANSPEED, + NVML_DEVICE_GET_TEMPERATURE, + NVML_DEVICE_GET_UTILIZATIONRATES, + NVML_DEVICE_GET_MEMORYINFO, + NVML_DEVICE_GET_PERFORMANCESTATE, + NVML_SYMBOL_COUNT +}; +typedef int (*local_init_t)(void); +typedef int (*local_shutdown_t)(void); +typedef int (*local_dev_get_count_t)(unsigned int *); +typedef int (*local_dev_get_handlebyindex_t)(unsigned int, nvmlDevice_t *); +typedef int (*local_dev_get_name_t)(nvmlDevice_t, char *, unsigned int); +typedef int (*local_dev_get_pciinfo_t)(nvmlDevice_t, nvmlPciInfo_t *); +typedef int (*local_dev_get_fanspeed_t)(nvmlDevice_t, unsigned int *); +typedef int (*local_dev_get_temperature_t)(nvmlDevice_t, nvmlTemperatureSensors_t, unsigned int *); +typedef int (*local_dev_get_utilizationrates_t)(nvmlDevice_t, nvmlUtilization_t *); +typedef int (*local_dev_get_memoryinfo_t)(nvmlDevice_t, nvmlMemory_t *); +typedef int (*local_dev_get_performancestate_t)(nvmlDevice_t, nvmlPstates_t *); + +static int +resolve_symbols(void) +{ + static void *nvml_dso; + int i; + + if (nvml_dso != NULL) + return 0; + if ((nvml_dso = dlopen("libnvidia-ml." DSOSUFFIX, RTLD_NOW)) == NULL) + return NVML_ERROR_LIBRARY_NOT_FOUND; + __pmNotifyErr(LOG_INFO, "Successfully loaded NVIDIA NVML library"); + for (i = 0; i < NVML_SYMBOL_COUNT; i++) + nvml_symtab[i].handle = dlsym(nvml_dso, nvml_symtab[i].symbol); + return 0; +} + +int +localNvmlInit(void) +{ + local_init_t init; + void *func; + int sts = resolve_symbols(); + + if (sts != 0) + return sts; + if ((func = nvml_symtab[NVML_INIT].handle) == NULL) + return NVML_ERROR_FUNCTION_NOT_FOUND; + init = (local_init_t)func; + return init(); +} + +int +localNvmlShutdown(void) +{ + local_shutdown_t shutdown; + void *func = nvml_symtab[NVML_SHUTDOWN].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + shutdown = (local_shutdown_t)func; + return shutdown(); +} + +int +localNvmlDeviceGetCount(unsigned int *count) +{ + local_dev_get_count_t dev_get_count; + void *func = nvml_symtab[NVML_DEVICE_GET_COUNT].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_count = (local_dev_get_count_t)func; + return dev_get_count(count); +} + +int +localNvmlDeviceGetHandleByIndex(unsigned int index, nvmlDevice_t *device) +{ + local_dev_get_handlebyindex_t dev_get_handlebyindex; + void *func = nvml_symtab[NVML_DEVICE_GET_HANDLEBYINDEX].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_handlebyindex = (local_dev_get_handlebyindex_t)func; + return dev_get_handlebyindex(index, device); +} + +int +localNvmlDeviceGetName(nvmlDevice_t device, char *name, unsigned int size) +{ + local_dev_get_name_t dev_get_name; + void *func = nvml_symtab[NVML_DEVICE_GET_NAME].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_name = (local_dev_get_name_t)func; + return dev_get_name(device, name, size); +} + +int +localNvmlDeviceGetPciInfo(nvmlDevice_t device, nvmlPciInfo_t *info) +{ + local_dev_get_pciinfo_t dev_get_pciinfo; + void *func = nvml_symtab[NVML_DEVICE_GET_PCIINFO].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_pciinfo = (local_dev_get_pciinfo_t)func; + return dev_get_pciinfo(device, info); +} + +int +localNvmlDeviceGetFanSpeed(nvmlDevice_t device, unsigned int *speed) +{ + local_dev_get_fanspeed_t dev_get_fanspeed; + void *func = nvml_symtab[NVML_DEVICE_GET_FANSPEED].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_fanspeed = (local_dev_get_fanspeed_t)func; + return dev_get_fanspeed(device, speed); +} + +int +localNvmlDeviceGetTemperature(nvmlDevice_t device, nvmlTemperatureSensors_t code, unsigned int *temp) +{ + local_dev_get_temperature_t dev_get_temperature; + void *func = nvml_symtab[NVML_DEVICE_GET_TEMPERATURE].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_temperature = (local_dev_get_temperature_t)func; + return dev_get_temperature(device, code, temp); +} + +int +localNvmlDeviceGetUtilizationRates(nvmlDevice_t device, nvmlUtilization_t *util) +{ + local_dev_get_utilizationrates_t dev_get_utilizationrates; + void *func = nvml_symtab[NVML_DEVICE_GET_UTILIZATIONRATES].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_utilizationrates = (local_dev_get_utilizationrates_t)func; + return dev_get_utilizationrates(device, util); +} + +int +localNvmlDeviceGetMemoryInfo(nvmlDevice_t device, nvmlMemory_t *memory) +{ + local_dev_get_memoryinfo_t dev_get_memoryinfo; + void *func = nvml_symtab[NVML_DEVICE_GET_MEMORYINFO].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_memoryinfo = (local_dev_get_memoryinfo_t)func; + return dev_get_memoryinfo(device, memory); +} + +int +localNvmlDeviceGetPerformanceState(nvmlDevice_t device, nvmlPstates_t *state) +{ + local_dev_get_performancestate_t dev_get_performancestate; + void *func = nvml_symtab[NVML_DEVICE_GET_PERFORMANCESTATE].handle; + + if (!func) + return NVML_ERROR_FUNCTION_NOT_FOUND; + dev_get_performancestate = (local_dev_get_performancestate_t)func; + return dev_get_performancestate(device, state); +} + +const char * +localNvmlErrStr(nvmlReturn_t sts) +{ + int i; + static const char *unknown = "No such error code"; + static struct { + int code; + const char *msg; + } table[] = { { + NVML_SUCCESS, +"The operation was successful" }, { + NVML_ERROR_UNINITIALIZED, +"NVML was not first initialized with nvmlInit()" }, { + NVML_ERROR_INVALID_ARGUMENT, +"A supplied argument is invalid" }, { + NVML_ERROR_NOT_SUPPORTED, +"The requested operation is not available on target device" }, { + NVML_ERROR_NO_PERMISSION, +"The current user does not have permission for operation" }, { + NVML_ERROR_ALREADY_INITIALIZED, +"Deprecated error code (5)" }, { + NVML_ERROR_NOT_FOUND, +"A query to find an object was unsuccessful" }, { + NVML_ERROR_INSUFFICIENT_SIZE, +"An input argument is not large enough" }, { + NVML_ERROR_INSUFFICIENT_POWER, +"A device's external power cables are not properly attached" }, { + NVML_ERROR_DRIVER_NOT_LOADED, +"NVIDIA driver is not loaded" }, { + NVML_ERROR_TIMEOUT, +"User provided timeout passed" }, { + NVML_ERROR_IRQ_ISSUE, +"NVIDIA Kernel detected an interrupt issue with a GPU" }, { + NVML_ERROR_LIBRARY_NOT_FOUND, +"NVML Shared Library couldn't be found or loaded" }, { + NVML_ERROR_FUNCTION_NOT_FOUND, +"Local version of NVML doesn't implement this function" }, { + NVML_ERROR_CORRUPTED_INFOROM, +"infoROM is corrupted" }, { + NVML_ERROR_GPU_IS_LOST, +"The GPU has fallen off the bus or has otherwise become inaccessible" }, { + NVML_ERROR_UNKNOWN, +"An internal driver error occurred" + } }; + + for (i = 0; i < (sizeof(table)/sizeof(table[0])); i++) { + if (table[i].code == sts) + return table[i].msg; + } + return unknown; +} diff --git a/src/pmdas/nvidia/localnvml.h b/src/pmdas/nvidia/localnvml.h new file mode 100644 index 0000000..3d108e5 --- /dev/null +++ b/src/pmdas/nvidia/localnvml.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _LOCAL_NVML_H +#define _LOCAL_NVML_H + +/* + * NVML interfaces and data structures, based on: + * http://docs.nvidia.com/deploy/nvml-api/index.html + */ + +#define NVML_DEVICE_NAME_BUFFER_SIZE 64 +#define NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE 16 + +typedef void *nvmlDevice_t; /* used as an opaque handle */ +typedef int nvmlPstates_t; /* performance state (0-15) */ + +/* Error codes */ +typedef enum { + NVML_SUCCESS = 0, + NVML_ERROR_UNINITIALIZED = 1, + NVML_ERROR_INVALID_ARGUMENT = 2, + NVML_ERROR_NOT_SUPPORTED = 3, + NVML_ERROR_NO_PERMISSION = 4, + NVML_ERROR_ALREADY_INITIALIZED = 5, + NVML_ERROR_NOT_FOUND = 6, + NVML_ERROR_INSUFFICIENT_SIZE = 7, + NVML_ERROR_INSUFFICIENT_POWER = 8, + NVML_ERROR_DRIVER_NOT_LOADED = 9, + NVML_ERROR_TIMEOUT = 10, + NVML_ERROR_IRQ_ISSUE = 11, + NVML_ERROR_LIBRARY_NOT_FOUND = 12, + NVML_ERROR_FUNCTION_NOT_FOUND = 13, + NVML_ERROR_CORRUPTED_INFOROM = 14, + NVML_ERROR_GPU_IS_LOST = 15, + NVML_ERROR_UNKNOWN = 999 +} nvmlReturn_t; + +typedef enum { + NVML_TEMPERATURE_GPU = 0, + NVML_TEMPERATURE_COUNT +} nvmlTemperatureSensors_t; + +typedef struct { + char busId[NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE]; + unsigned int domain; + unsigned int bus; + unsigned int device; + unsigned int pciDeviceId; + unsigned int pciSubSystemId; + unsigned int reserved[4]; +} nvmlPciInfo_t; + +typedef struct { + unsigned int gpu; + unsigned int memory; +} nvmlUtilization_t; + +typedef struct { + unsigned long long total; + unsigned long long free; + unsigned long long used; +} nvmlMemory_t; + +extern int localNvmlInit(void); +extern int localNvmlShutdown(void); +extern const char *localNvmlErrStr(nvmlReturn_t); + +extern int localNvmlDeviceGetCount(unsigned int *); +extern int localNvmlDeviceGetHandleByIndex(unsigned int, nvmlDevice_t *); +extern int localNvmlDeviceGetName(nvmlDevice_t, char *, unsigned int); +extern int localNvmlDeviceGetPciInfo(nvmlDevice_t, nvmlPciInfo_t *); +extern int localNvmlDeviceGetFanSpeed(nvmlDevice_t, unsigned int *); +extern int localNvmlDeviceGetTemperature(nvmlDevice_t, nvmlTemperatureSensors_t, unsigned int *); +extern int localNvmlDeviceGetUtilizationRates(nvmlDevice_t, nvmlUtilization_t *); +extern int localNvmlDeviceGetMemoryInfo(nvmlDevice_t, nvmlMemory_t *); +extern int localNvmlDeviceGetPerformanceState(nvmlDevice_t, nvmlPstates_t *); + +#endif /* _LOCAL_NVML_H */ diff --git a/src/pmdas/nvidia/nvidia.c b/src/pmdas/nvidia/nvidia.c new file mode 100644 index 0000000..849f51f --- /dev/null +++ b/src/pmdas/nvidia/nvidia.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "localnvml.h" + +/* InDom table (just one row - corresponding to the set of graphics cards) */ +enum { GCARD_INDOM = 0 }; +pmdaIndom indomtab[] = { + { GCARD_INDOM, 0, NULL }, +}; + +/* List of metric item numbers - increasing from zero, no holes */ +enum { + NVIDIA_NUMCARDS = 0, + NVIDIA_CARDID, + NVIDIA_CARDNAME, + NVIDIA_BUSID, + NVIDIA_TEMP, + NVIDIA_FANSPEED, + NVIDIA_PERFSTATE, + NVIDIA_GPUACTIVE, + NVIDIA_MEMACTIVE, + NVIDIA_MEMUSED, + NVIDIA_MEMTOTAL, + NVIDIA_MEMFREE, + + NVIDIA_METRIC_COUNT +}; + +/* Table of metrics exported by this PMDA */ +static pmdaMetric metrictab[] = { + { NULL, { PMDA_PMID(0, NVIDIA_NUMCARDS), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_CARDID), PM_TYPE_U32, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_CARDNAME), PM_TYPE_STRING, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_BUSID), PM_TYPE_STRING, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_TEMP), PM_TYPE_U32, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_FANSPEED), PM_TYPE_U32, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_PERFSTATE), PM_TYPE_U32, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_GPUACTIVE), PM_TYPE_U32, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_MEMACTIVE), PM_TYPE_U32, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_MEMUSED), PM_TYPE_U64, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_MEMTOTAL), PM_TYPE_U64, GCARD_INDOM, + PM_SEM_DISCRETE, PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + { NULL, { PMDA_PMID(0, NVIDIA_MEMFREE), PM_TYPE_U64, GCARD_INDOM, + PM_SEM_INSTANT, PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +}; + +/* GCARD_INDOM struct, stats that are per card */ +typedef struct { + int cardid; + int failed[NVIDIA_METRIC_COUNT]; + char *name; + char *busid; + int temp; + int fanspeed; + int perfstate; + nvmlUtilization_t active; + nvmlMemory_t memory; +} nvinfo_t; + +/* overall struct, holds instance values, indom and instance struct arrays */ +typedef struct { + int numcards; + int maxcards; + nvinfo_t *nvinfo; + pmdaIndom *nvindom; +} pcp_nvinfo_t; + +static pcp_nvinfo_t pcp_nvinfo; +static char mypath[MAXPATHLEN]; +static int isDSO = 1; +static int nvmlDSO_loaded; + +static int +setup_gcard_indom(void) +{ + unsigned int device_count = 0; + pmdaIndom *idp = &indomtab[GCARD_INDOM]; + char gpuname[32], *name; + size_t size; + int i, sts; + + /* Initialize instance domain and instances. */ + if ((sts = localNvmlDeviceGetCount(&device_count)) != NVML_SUCCESS) { + __pmNotifyErr(LOG_ERR, "nvmlDeviceGetCount: %s", + localNvmlErrStr(sts)); + return sts; + } + + pcp_nvinfo.nvindom = idp; + pcp_nvinfo.nvindom->it_numinst = 0; + + size = device_count * sizeof(pmdaInstid); + pcp_nvinfo.nvindom->it_set = (pmdaInstid *)malloc(size); + if (!pcp_nvinfo.nvindom->it_set) { + __pmNoMem("gcard indom", size, PM_RECOV_ERR); + return -ENOMEM; + } + + size = device_count * sizeof(nvinfo_t); + if ((pcp_nvinfo.nvinfo = (nvinfo_t *)malloc(size)) == NULL) { + __pmNoMem("gcard values", size, PM_RECOV_ERR); + free(pcp_nvinfo.nvindom->it_set); + return -ENOMEM; + } + memset(pcp_nvinfo.nvinfo, 0, size); + + for (i = 0; i < device_count; i++) { + pcp_nvinfo.nvindom->it_set[i].i_inst = i; + snprintf(gpuname, sizeof(gpuname), "gpu%d", i); + if ((name = strdup(gpuname)) == NULL) { + __pmNoMem("gcard instname", strlen(gpuname), PM_RECOV_ERR); + while (--i) + free(pcp_nvinfo.nvindom->it_set[i].i_name); + free(pcp_nvinfo.nvindom->it_set); + free(pcp_nvinfo.nvinfo); + return -ENOMEM; + } + pcp_nvinfo.nvindom->it_set[i].i_name = name; + } + + pcp_nvinfo.numcards = 0; + pcp_nvinfo.maxcards = device_count; + pcp_nvinfo.nvindom->it_numinst = device_count; + return 0; +} + +static int +refresh(pcp_nvinfo_t *pcp_nvinfo) +{ + unsigned int device_count; + nvmlDevice_t device; + char name[NVML_DEVICE_NAME_BUFFER_SIZE]; + nvmlPciInfo_t pci; + unsigned int fanspeed; + unsigned int temperature; + nvmlUtilization_t utilization; + nvmlMemory_t memory; + nvmlPstates_t pstate; + int i, sts; + + if (!nvmlDSO_loaded) { + if (localNvmlInit() == NVML_ERROR_LIBRARY_NOT_FOUND) + return 0; + setup_gcard_indom(); + nvmlDSO_loaded = 1; + } + + if ((sts = localNvmlDeviceGetCount(&device_count)) != 0) { + __pmNotifyErr(LOG_ERR, "nvmlDeviceGetCount: %s", + localNvmlErrStr(sts)); + return sts; + } + pcp_nvinfo->numcards = device_count; + + for (i = 0; i < device_count && i < pcp_nvinfo->maxcards; i++) { + pcp_nvinfo->nvinfo[i].cardid = i; + if ((sts = localNvmlDeviceGetHandleByIndex(i, &device))) { + __pmNotifyErr(LOG_ERR, "nvmlDeviceGetHandleByIndex: %s", + localNvmlErrStr(sts)); + memset(pcp_nvinfo->nvinfo[i].failed, 1, NVIDIA_METRIC_COUNT); + continue; + } + memset(pcp_nvinfo->nvinfo[i].failed, 0, NVIDIA_METRIC_COUNT); + if ((sts = localNvmlDeviceGetName(device, name, sizeof(name)))) + pcp_nvinfo->nvinfo[i].failed[NVIDIA_CARDNAME] = 1; + if ((sts = localNvmlDeviceGetPciInfo(device, &pci))) + pcp_nvinfo->nvinfo[i].failed[NVIDIA_BUSID] = 1; + if ((sts = localNvmlDeviceGetFanSpeed(device, &fanspeed))) + pcp_nvinfo->nvinfo[i].failed[NVIDIA_FANSPEED] = 1; + if ((sts = localNvmlDeviceGetTemperature(device, NVML_TEMPERATURE_GPU, &temperature))) + pcp_nvinfo->nvinfo[i].failed[NVIDIA_TEMP] = 1; + if ((sts = localNvmlDeviceGetUtilizationRates(device, &utilization))) { + pcp_nvinfo->nvinfo[i].failed[NVIDIA_GPUACTIVE] = 1; + pcp_nvinfo->nvinfo[i].failed[NVIDIA_MEMACTIVE] = 1; + } + if ((sts = localNvmlDeviceGetMemoryInfo(device, &memory))) { + pcp_nvinfo->nvinfo[i].failed[NVIDIA_MEMUSED] = 1; + pcp_nvinfo->nvinfo[i].failed[NVIDIA_MEMTOTAL] = 1; + pcp_nvinfo->nvinfo[i].failed[NVIDIA_MEMFREE] = 1; + } + if ((sts = localNvmlDeviceGetPerformanceState(device, &pstate))) + pcp_nvinfo->nvinfo[i].failed[NVIDIA_PERFSTATE] = 1; + + if (pcp_nvinfo->nvinfo[i].name == NULL) + pcp_nvinfo->nvinfo[i].name = strdup(name); + if (pcp_nvinfo->nvinfo[i].busid == NULL) + pcp_nvinfo->nvinfo[i].busid = strdup(pci.busId); + pcp_nvinfo->nvinfo[i].temp = temperature; + pcp_nvinfo->nvinfo[i].fanspeed = fanspeed; + pcp_nvinfo->nvinfo[i].perfstate = pstate; + pcp_nvinfo->nvinfo[i].active = utilization; /* struct copy */ + pcp_nvinfo->nvinfo[i].memory = memory; /* struct copy */ + } + + return 0; +} + +/* + * Wrapper for pmdaFetch which refresh the set of values once per fetch + * PDU. The fetchCallback is then called once per-metric/instance pair + * to perform the actual filling of the pmResult (via each pmAtomValue). + */ +static int +nvidia_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + refresh(&pcp_nvinfo); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +nvidia_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (idp->cluster != 0) + return PM_ERR_PMID; + if (idp->item != 0 && inst > indomtab[GCARD_INDOM].it_numinst) + return PM_ERR_INST; + + switch (idp->item) { + case NVIDIA_NUMCARDS: + atom->ul = pcp_nvinfo.numcards; + break; + case NVIDIA_CARDID: + atom->ul = pcp_nvinfo.nvinfo[inst].cardid; + break; + case NVIDIA_CARDNAME: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_CARDNAME]) + return PM_ERR_VALUE; + atom->cp = pcp_nvinfo.nvinfo[inst].name; + break; + case NVIDIA_BUSID: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_BUSID]) + return PM_ERR_VALUE; + atom->cp = pcp_nvinfo.nvinfo[inst].busid; + break; + case NVIDIA_TEMP: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_TEMP]) + return PM_ERR_VALUE; + atom->ul = pcp_nvinfo.nvinfo[inst].temp; + break; + case NVIDIA_FANSPEED: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_FANSPEED]) + return PM_ERR_VALUE; + atom->ul = pcp_nvinfo.nvinfo[inst].fanspeed; + break; + case NVIDIA_PERFSTATE: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_PERFSTATE]) + return PM_ERR_VALUE; + atom->ul = pcp_nvinfo.nvinfo[inst].perfstate; + break; + case NVIDIA_GPUACTIVE: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_GPUACTIVE]) + return PM_ERR_VALUE; + atom->ul = pcp_nvinfo.nvinfo[inst].active.gpu; + break; + case NVIDIA_MEMACTIVE: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_MEMACTIVE]) + return PM_ERR_VALUE; + atom->ul = pcp_nvinfo.nvinfo[inst].active.memory; + break; + case NVIDIA_MEMUSED: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_MEMUSED]) + return PM_ERR_VALUE; + atom->ull = pcp_nvinfo.nvinfo[inst].memory.used; + break; + case NVIDIA_MEMTOTAL: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_MEMTOTAL]) + return PM_ERR_VALUE; + atom->ull = pcp_nvinfo.nvinfo[inst].memory.total; + break; + case NVIDIA_MEMFREE: + if (pcp_nvinfo.nvinfo[inst].failed[NVIDIA_MEMFREE]) + return PM_ERR_VALUE; + atom->ull = pcp_nvinfo.nvinfo[inst].memory.free; + break; + default: + return PM_ERR_PMID; + } + + return 0; +} + +/** + * Initializes the path to the help file for this PMDA. + */ +static void +initializeHelpPath() +{ + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "nvidia" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); +} + +void +__PMDA_INIT_CALL +nvidia_init(pmdaInterface *dp) +{ + int sts; + + if (isDSO) { + initializeHelpPath(); + pmdaDSO(dp, PMDA_INTERFACE_2, "nvidia DSO", mypath); + } + + if (dp->status != 0) + return; + + if ((sts = localNvmlInit()) == NVML_SUCCESS) { + setup_gcard_indom(); + nvmlDSO_loaded = 1; + } + else { + /* + * This is OK, just continue on until it *is* installed; + * until that time, simply report "no values available". + */ + __pmNotifyErr(LOG_INFO, "NVIDIA NVML library currently unavailable"); + } + + dp->version.any.fetch = nvidia_fetch; + pmdaSetFetchCallBack(dp, nvidia_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +static pmdaOptions opts = { + .short_options = "D:d:l:?", + .long_options = longopts, +}; + +int +main(int argc, char **argv) +{ + pmdaInterface desc; + + isDSO = 0; + __pmSetProgname(argv[0]); + + initializeHelpPath(); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, NVML, + "nvidia.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + pmdaOpenLog(&desc); + pmdaConnect(&desc); + nvidia_init(&desc); + pmdaMain(&desc); + + exit(0); +} diff --git a/src/pmdas/nvidia/pmns b/src/pmdas/nvidia/pmns new file mode 100644 index 0000000..b496cae --- /dev/null +++ b/src/pmdas/nvidia/pmns @@ -0,0 +1,30 @@ +/* + * Metrics for nvidia GPU PMDA + * + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +nvidia { + numcards NVML:0:0 + gpuid NVML:0:1 + cardname NVML:0:2 + busid NVML:0:3 + temp NVML:0:4 + fanspeed NVML:0:5 + perfstate NVML:0:6 + gpuactive NVML:0:7 + memactive NVML:0:8 + memused NVML:0:9 + memtotal NVML:0:10 + memfree NVML:0:11 +} diff --git a/src/pmdas/nvidia/root b/src/pmdas/nvidia/root new file mode 100644 index 0000000..fe12bc2 --- /dev/null +++ b/src/pmdas/nvidia/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { nvidia } + +#include "pmns" + diff --git a/src/pmdas/oracle/GNUmakefile b/src/pmdas/oracle/GNUmakefile new file mode 100644 index 0000000..052cf82 --- /dev/null +++ b/src/pmdas/oracle/GNUmakefile @@ -0,0 +1,49 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = oracle +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/oracle/Install b/src/pmdas/oracle/Install new file mode 100755 index 0000000..a64aac0 --- /dev/null +++ b/src/pmdas/oracle/Install @@ -0,0 +1,63 @@ +#! /bin/sh +# +# Copyright (c) 2009,2012 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Oracle PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=oracle +user=$iam +domain=32 +perl_opt=true +daemon_opt=false +forced_restart=true + +perl -e "use DBI" 2>/dev/null +if test $? -ne 0; then + echo "Perl database interface (DBI) is not installed" + exit 1 +fi + +perl -e "use DBD::Oracle" 2>/dev/null +if test $? -ne 0; then + echo "Oracle database driver (DBD::Oracle) is not installed" + exit 1 +fi + +su -c id $user >/dev/null 2>&1 +if test $? -ne 0; then + echo "Cannot change user to $user, sorry this is fatal" + exit 1 +fi + +su -c mkdir -p "$PCP_VAR_DIR/config/pmda" 2>/dev/null +indoms="0 1 2 3 4 5 6 7 8 9 10" +for indom in $indoms +do + touch "$PCP_VAR_DIR/config/pmda/$domain.$indom" >/dev/null 2>&1 + if test $? -ne 0; then + echo "Cannot create indom persistance files as user $user" + echo "Failed on: $PCP_VAR_DIR/config/pmda/$domain.$indom" + exit 1 + fi +done + +# TODO: need to ask for user/pass and SIDs, write $PCP_VAR_DIR/config/oracle/oracle.conf +# (unless it exists) + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/oracle/Remove b/src/pmdas/oracle/Remove new file mode 100755 index 0000000..131c6e0 --- /dev/null +++ b/src/pmdas/oracle/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Oracle PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=oracle + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/oracle/pmdaoracle.pl b/src/pmdas/oracle/pmdaoracle.pl new file mode 100644 index 0000000..b45be2f --- /dev/null +++ b/src/pmdas/oracle/pmdaoracle.pl @@ -0,0 +1,2364 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2009,2012 Aconex. All Rights Reserved. +# Copyright (c) 1998 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use DBI; + +my $username = 'SYSTEM'; +my $password = 'manager'; +my @sids = ( 'master' ); + +# Configuration files for overriding the above settings +for my $file ( '/etc/pcpdbi.conf', # system defaults (lowest priority) + pmda_config('PCP_PMDAS_DIR') . '/oracle/oracle.conf', + pmda_config('PCP_VAR_DIR') . '/config/oracle/oracle.conf', + './oracle.conf' ) { # current directory (high priority) + eval `cat $file` unless ! -f $file; +} + +use vars qw( $pmda %sid_instances ); +use vars qw( %latch_instances %file_instances %rollback_instances ); +use vars qw( %reqdist_instances %rowcache_instances %session_instances ); +use vars qw( %object_cache_instances %system_event_instances ); +use vars qw( %librarycache_instances %waitstat_instances ); + +my $latch_indom = 0; +my $file_indom = 1; +my $rollback_indom = 2; +my $reqdist_indom = 3; +my $rowcache_indom = 4; +my $session_indom = 5; +my $object_cache_indom = 6; +my $system_event_indom = 7; +my $librarycache_indom = 8; +my $waitstat_indom = 9; +my $sid_indom = 10; + +my @novalues = (); +my %object_cache_instances = ( + 'INDEX' => \@novalues, 'TABLE' => \@novalues, + 'CLUSTER' => \@novalues, 'VIEW' => \@novalues, + 'SET' => \@novalues, 'SYNONYM' => \@novalues, + 'SEQUENCE' => \@novalues, 'PROCEDURE' => \@novalues, + 'FUNCTION' => \@novalues, 'PACKAGE' => \@novalues, + 'PACKAGE_BODY' => \@novalues, 'TRIGGER' => \@novalues, + 'CLASS' => \@novalues, 'OBJECT' => \@novalues, + 'USER' => \@novalues, 'DBLINK' => \@novalues, + 'NON-EXISTENT' => \@novalues, 'NOT_LOADED' => \@novalues, + 'OTHER' => \@novalues ); + +my %sids_by_name; +my %tables_by_name = ( + 'sysstat' => { + insts_handle => undef, fetch_handle => undef, values => {}, + # insts => SID indom is a static array + fetch => 'SELECT statistic#, value FROM v$sysstat' }, + 'license' => { + insts_handle => undef, fetch_handle => undef, values => {}, }, + 'latch' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT latch#, name FROM v$latch', + fetch => 'SELECT latch#, name FROM v$latch' }, + 'filestat' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT file#, name FROM v$datafile', + fetch => 'SELECT file#, phyrds, phywrts, phyblkrd,' . + ' phyblkwrt, readtim, writetim' . + ' FROM v$filestat' }, + 'rollstat' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT usn, name FROM v$rollname', + fetch => 'SELECT usn, rssize, writes, xacts, gets, waits, hwmsize,' . + ' shrinks, wraps, extends, aveshrink, aveactive' . + ' FROM v$rollstat' }, + 'reqdist' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT bucket FROM v$reqdist', + fetch => 'SELECT bucket, count FROM v$reqdist' }, + 'backup' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT file# FROM v$backup', + fetch => 'SELECT file#, status FROM v$backup' }, + 'rowcache' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT cache#, subordinate#, parameter FROM v$rowcache', + fetch => 'SELECT cache#, subordinate#, count, gets, getmisses,' . + ' scans, scanmisses' . + ' FROM v$rowcache' }, + 'sesstat' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT sid FROM v$session', + fetch => 'SELECT sid, statistic#, value FROM v$sesstat' }, + 'object_cache' => { + insts_handle => undef, fetch_handle => undef, values => {}, + # insts => object_cache indom is a static array + fetch => 'SELECT type, sharable_mem, loads, locks, pins' . + ' FROM v$db_object_cache' }, + 'system_event' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT event#,name FROM v$event_name', + fetch => 'SELECT event_id, event, total_waits, total_timeouts,' . + ' time_waited, average_wait' . + ' FROM v$system_event' }, + 'version' => { + insts_handle => undef, fetch_handle => undef, values => {}, + fetch => 'SELECT DISTINCT banner INTO :pc_version' . + ' FROM v$version WHERE banner LIKE \'Oracle%\'' }, + 'librarycache' => { + insts_handle => undef, fetch_handle => undef, values => {}, + insts => 'SELECT namespace FROM v$librarycache', + fetch => 'SELECT namespace, gets, gethits, gethitratio, pins,' . + ' pinhits, pinhitratio, reloads, invalidations' . + ' FROM v$librarycache' }, +); + +my %tables_by_cluster = ( + '0' => { + name => 'sysstat', + setup => \&setup_sysstat, + indom => $sid_indom, + values => \&sysstat_values }, + '1' => { + name => 'license', + setup => \&setup_license, + indom => $sid_indom, + values => \&license_values }, + '2' => { + name => 'latch', + setup => \&setup_latch, + indom => $latch_indom, + values => \&latch_values }, + '3' => { + name => 'filestat', + setup => \&setup_filestat, + indom => $file_indom, + values => \&filestat_values }, + '4' => { + name => 'rollstat', + setup => \&setup_rollstat, + indom => $rollback_indom, + values => \&rollstat_values }, + '5' => { + name => 'reqdist', + setup => \&setup_reqdist, + indom => $reqdist_indom, + values => \&reqdist_values }, + '6' => { + name => 'backup', + setup => \&setup_backup, + indom => 'backup', + values => \&backup_values }, + '7' => { + name => 'rowcache', + setup => \&setup_rowcache, + indom => $rowcache_indom, + values => \&rowcache_values }, +# '8' => { +# name => 'sesstat', +# setup => \&setup_sesstat, +# indom => $session_indom, +# values => \&sesstat_values }, + '9' => { + name => 'object_cache', + setup => \&setup_object_cache, + indom => $object_cache_indom, + values => \&object_cache_values }, + '10' => { + name => 'system_event', + setup => \&setup_system_event, + indom => $system_event_indom, + insts => \&system_event_insts, + values => \&system_event_values }, + '11' => { + name => 'version', + setup => \&setup_version, + indom => $sid_indom, + values => \&version_values }, + '12' => { + name => 'librarycache', + setup => \&setup_librarycache, + indom => $librarycache_indom, + values => \&librarycache_values }, + '13' => { + name => 'waitstat', + setup => \&setup_waitstat, + indom => $waitstat_indom, + values => \&waitstat_values }, +); + +sub oracle_connection_setup +{ + foreach my $sid (@sids) { + my $db = $sids_by_name{$sid}{db_handle}; + $db = oracle_sid_connection_setup($sid, $db); + $sids_by_name{$sid}{db_handle} = $db; + } +} + +sub oracle_sid_connection_setup +{ + my $sid = shift; + my $dbh = shift; + + if (!defined($dbh)) { + $dbh = DBI->connect("dbi:Oracle:$sid", $username, $password); + if (defined($dbh)) { + $pmda->log("Oracle connection established\n"); + + foreach my $key (keys %tables_by_name) { + my ( $query, $insts_query, $fetch_query ); + + $insts_query = $tables_by_name{$key}{insts}; + $fetch_query = $tables_by_name{$key}{fetch}; + + if (defined($insts_query)) { + $query = $dbh->prepare($fetch_query); + $tables_by_name{$key}{insts_handle} = $query + unless (!defined($query)); + } + if (defined($fetch_query)) { + $query = $dbh->prepare($fetch_query); + $tables_by_name{$key}{fetch_handle} = $query + unless (!defined($query)); + } + } + } + } + return $dbh; +} + +sub oracle_refresh +{ + my ($cluster) = @_; + + foreach my $sid (@sids) { + my $db = $sids_by_name{$sid}{db_handle}; + my $name = $tables_by_cluster{"$cluster"}{name}; + my $refresh = $tables_by_cluster{"$cluster"}{values}; + &$refresh($db, $sid, $tables_by_name{$name}{fetch_handle}); + } +} + +sub oracle_fetch_callback +{ + my ( $cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ( $indom, $table, $value, $valueref, @columns ); + + return (PM_ERR_PMID, 0) unless defined($metric_name); + $table = $metric_name; + $table =~ s/^oracle\.//; + $table =~ s/\.*$//; + + # $pmda->log("fetch_cb $metric_name $cluster:$item ($inst) - $table\n"); + $indom = $tables_by_cluster{"$cluster"}{indom}; + $valueref = pmda_inst_lookup($indom, $inst); + return (PM_ERR_INST, 0) unless defined($valueref); + @columns = @$valueref; + $value = $columns[$item]; + return (PM_ERR_APPVERSION, 0) unless defined($value); + + return ($value, 1); +} + +# +# Refresh routines - one per table (cluster) - format database query +# result set for later use by the generic fetch callback routine. +# +sub refresh_results +{ + my ( $dbh, $handle ) = @_; + + return undef + unless (defined($dbh) && defined($handle) && defined($handle->execute())); + return $handle->fetchall_arrayref(); +} + +sub system_event_values +{ + my ( $dbh, $sid, $handle ) = @_; + my $result = refresh_results($dbh, $handle); + + %system_event_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $eventid = $result->[$i][0]; + my $eventname = $result->[$i][1]; + my $instname = "$sid/$eventid $eventname"; + my @values = @$result[$i]; + $system_event_instances{$instname} = \@values; + } + } + $pmda->replace_indom($system_event_indom, \%system_event_instances); +} + +sub system_event_insts +{ + my ( $dbh, $sid, $handle ) = @_; + my $result = refresh_results($dbh, $handle); + + %system_event_instances = (); + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $eventid = $result->[$i][0]; + my $eventname = $result->[$i][1]; + my $instname = "$sid/$eventid $eventname"; + $system_event_instances{$instname} = \@novalues; + } + } + $pmda->replace_indom($system_event_indom, \%system_event_instances); +} + +sub license_values { } +sub license_insts { } + + + +sub oracle_indoms_setup +{ + $pmda->add_indom($latch_indom, \%latch_instances, + 'Instance domain "latch" from Oracle PMDA', +'The latches used by the RDBMS. The latch instance domain does not +change. Latches are simple, low-level serialization mechanisms which +protect access to structures in the system global area (SGA).'); + + $pmda->add_indom($file_indom, \%file_instances, + 'Instance domain "file" from Oracle PMDA', +'The collection of data files that make up the database. This instance +domain may change during database operation as files are added to or +removed.'); + + $pmda->add_indom($rollback_indom, \%rollback_instances, + 'Instance domain "rollback" from Oracle PMDA', +'The collection of rollback segments for the database. This instance +domain may change during database operation as segments are added to or +removed.'); + + $pmda->add_indom($reqdist_indom, \%reqdist_instances, + 'RDBMS Request Distribution from Oracle PMDA', +'Each instance is one of the buckets in the histogram of RDBMS request +service times. The instances are named according to the longest +service time that will be inserted into its bucket. The instance +domain does not change.'); + + $pmda->add_indom($rowcache_indom, \%rowcache_instances, + 'Instance domain "rowcache" from Oracle PMDA', +'Each instance is a type of data dictionary cache. The names are +derived from the database parameters that define the number of entries +in the particular cache. In some cases subordinate caches exist. +Names for such sub-caches are composed of the subordinate cache +parameter name prefixed with parent cache name with a "." as a +separator. Each cache has an identifying number which appears in +parentheses after the textual portion of the cache name to resolve +naming ambiguities. The rowcache instance domain does not change.'); + + $pmda->add_indom($session_indom, \%session_instances, + 'Instance domain "session" from Oracle PMDA', +'Each instance is a session to the Oracle database. Sessions may come +and go rapidly. The instance names correspond to the numeric Oracle +session identifiers.'); + + $pmda->add_indom($object_cache_indom, \%object_cache_instances, + 'Instance domain "cache objects" from Oracle PMDA', +'The various types of objects in the database object cache. This +includes such objects as indices, tables, procedures, packages, users +and dblink. Any object types not recognized by the Oracle PMDA are +grouped together into a special instance named "other". The instance +domain may change as various types of objects are bought into and +flushed out of the database object cache.'); + + $pmda->add_indom($system_event_indom, \%system_event_instances, + 'Instance domain "system events" from Oracle PMDA', +'The various system events which the database may wait on. This +includes events such as interprocess communication, control file I/O, +log file I/O, timers.'); + + $pmda->add_indom($librarycache_indom, \%librarycache_instances, + 'Instance domain "librarycache" from Oracle PMDA', ''); + $pmda->add_indom($waitstat_indom, \%waitstat_instances, + 'Instance domain "wait statistics" from Oracle PMDA', ''); + + $pmda->add_indom($sid_indom, \%sid_instances, + 'Instance domain "SID" from Oracle PMDA', +'The system identifiers used by the RDBMS and monitored by this PMDA.'); +} + +sub oracle_metrics_setup +{ + foreach my $cluster (sort (keys %tables_by_cluster)) { + my $setup = $tables_by_cluster{"$cluster"}{setup}; + my $indom = $tables_by_cluster{"$cluster"}{indom}; + &$setup($cluster, $indom, $tables_by_cluster{"$cluster"}{params}); + } +} + +# +# Setup routines - one per cluster, add metrics to PMDA +# + +sub setup_waitstat # block contention stats from v$waitstat +{ + $pmda->add_metric(pmda_pmid(13,0), PM_TYPE_U32, $waitstat_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.waitstat.count', + 'Number of waits for each block class', +'The number of waits for each class of block. This value is obtained +from the COUNT column of the V$WAITSTAT view.'); + + $pmda->add_metric(pmda_pmid(13,1), PM_TYPE_U32, $waitstat_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.waitstat.time', + 'Sum of all wait times for each block class', +'The sum of all wait times for each block class. This value is obtained +from the TIME column of the V$WAITSTAT view.'); +} + +sub setup_version # version data from the v$version view +{ + $pmda->add_metric(pmda_pmid(11,0), PM_TYPE_STRING, $sid_indom, + PM_SEM_DISCRETE, pmda_units(0,0,0,0,0,0), + 'oracle.version', + 'Oracle component name and version number', ''); +} + +sub setup_system_event # statistics from v$system_event +{ + $pmda->add_metric(pmda_pmid(10,0), PM_TYPE_U32, $system_event_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.event.waits', + 'Number of waits for various system events', +'The total number of waits for various system events. This value is +obtained from the TOTAL_WAITS column of the V$SYSTEM_EVENT view.'); + + $pmda->add_metric(pmda_pmid(10,1), PM_TYPE_U32, $system_event_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.event.timeouts', + 'Number of timeouts for various system events', +'The total number of timeouts for various system events. This value is +obtained from the TOTAL_TIMEOUTS column of the V$SYSTEM_EVENT view.'); + + $pmda->add_metric(pmda_pmid(10,2), PM_TYPE_U32, $system_event_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.event.time_waited', + 'Total time waited for various system events', +'The total amount of time waited for various system events. This value +is obtained from the TIME_WAITED column of the V$SYSTEM_EVENT view and +converted to units of milliseconds.'); + + $pmda->add_metric(pmda_pmid(10,3), PM_TYPE_FLOAT, $system_event_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.event.average_wait', + 'Average time waited for various system events', +'The average time waited for various system events. This value is +obtained from the AVERAGE_WAIT column of the V$SYSTEM_EVENT view +and converted to units of milliseconds.'); +} + +sub setup_sysstat ## statistics from v$sysstat +{ + $pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.logons', 'Total cumulative logons', +'The "logons cumulative" statistic from the V$SYSSTAT view. This is the +total number of logons since the instance started.'); + + $pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.curlogons', 'Total current logons', +'The "logons current" statistic from the V$SYSSTAT view. This is the +total number of current logons.'); + + $pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.opencursors', 'Total cumulative opened cursors', +'The "opened cursors cumulative" statistic from the V$SYSSTAT view. +This is the total number of cursors opened since the instance started.'); + + $pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.current_cursors', 'Total current open cursors', +'The "opened cursors current" statistic from the V$SYSSTAT view. This +is the total number of current open cursors.'); + + $pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.user_commits', 'Total user commits', +'The "user commits" statistic from the V$SYSSTAT view. When a user +commits a transaction, the redo generated that reflects the changes +made to database blocks must be written to disk. Commits often +represent the closest thing to a user transaction rate.'); + + $pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.user_rollbacks', 'Total user rollbacks', +'The "user rollbacks" statistic from the V$SYSSTAT view. This statistic +stores the number of times users manually issue the ROLLBACK statement +or an error occurs during users\' transactions.'); + + $pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.user_calls', 'Total user calls', +'The "user calls" statistic from the V$SYSSTAT view. Oracle allocates +resources (Call State Objects) to keep track of relevant user call data +structures every time you log in, parse or execute. When determining +activity, the ratio of user calls to RPI calls, gives you an indication +of how much internal work gets generated as a result of the type of +requests the user is sending to Oracle.'); + + $pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.recursecalls', 'Total recursive calls', +'The "recursive calls" statistic from the V$SYSSTAT view. Oracle +maintains tables used for internal processing. When Oracle needs to +make a change to these tables, it internally generates an SQL +statement. These internal SQL statements generate recursive calls.'); + + $pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.recursecpu', 'Total recursive cpu usage', +'The "recursive cpu usage" statistic from the V$SYSSTAT view. The total +CPU time used by non-user calls (recursive calls). Subtract this value +from oracle.sysstat.sessioncpu to determine how much CPU time was used +by the user calls. Units are milliseconds of CPU time.'); + + $pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.session.lreads', 'Total session logical reads', +'The "session logical reads" statistic from the V$SYSSTAT view. This +statistic is basically the sum of oracle.systat.dbbgets and +oracle.sysstat.consgets. Refer to the help text for these +individual metrics for more information.'); + + $pmda->add_metric(pmda_pmid(0,10), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.session.procspace', +'Total session stored procedure space', +'The "session stored procedure space" statistic from the V$SYSSTAT +view. This metric shows the amount of memory that this session is +using for stored procedures.'); + + $pmda->add_metric(pmda_pmid(0,11), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.cpucall', 'CPU used when call started', +'The "CPU used when call started" statistic from the V$SYSSTAT view. +This is the session CPU when current call started. Units are +milliseconds of CPU time.'); + + $pmda->add_metric(pmda_pmid(0,12), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.session.cpu', 'Total CPU used by this session', +'The "CPU used by this session" statistic from the V$SYSSTAT view. This +is the amount of CPU time used by a session between when a user call +started and ended. Units for the exported metric are milliseconds, but +Oracle uses an internal resolution of tens of milliseconds and some +user calls can complete within 10 milliseconds, resulting in the start +and end user-call times being the same. In this case, zero +milliseconds are added to the statistic.'); + + $pmda->add_metric(pmda_pmid(0,13), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'oracle.sysstat.session.contime', 'Session connect time', +'The "session connect time" statistic from the V$SYSSTAT view. +Wall clock time of when session logon occured. Units are seconds +since the epoch.'); + + $pmda->add_metric(pmda_pmid(0,14), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'oracle.sysstat.procidle', 'Total process last non-idle time', +'The "process last non-idle time" statistic from the V$SYSSTAT view. +This is the last time this process was not idle. Units are seconds +since the epoch.'); + + $pmda->add_metric(pmda_pmid(0,15), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.session.mem', 'Session UGA memory', +'The "session UGA memory" statistic from the V$SYSSTAT view. This +shows the current session UGA (User Global Area) memory size.'); + + $pmda->add_metric(pmda_pmid(0,16), PM_TYPE_U32, $sid_indom, + PM_SEM_DISCRETE, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.session.maxmem', 'Maximum session UGA memory', +'The "session UGA memory max" statistic from the V$SYSSTAT view. This +shows the maximum session UGA (User Global Area) memory size.'); + + $pmda->add_metric(pmda_pmid(0,17), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.msgxmit', 'Total messages sent', +'The "messages sent" statistic from the V$SYSSTAT view. This is the +total number of messages sent between Oracle processes. A message is +sent when one Oracle process wants to post another to perform some +action.'); + + $pmda->add_metric(pmda_pmid(0,18), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.msgrecv', 'Total messages received', +'The "messages received" statistic from the V$SYSSTAT view. This is the +total number of messages received. A message is sent when one Oracle +process wants to post another to perform some action.'); + + $pmda->add_metric(pmda_pmid(0,19), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.bgtimeouts', 'Total background timeouts', +'The "background timeouts" statistic from the V$SYSSTAT view. This is +a count of the times where a background process has set an alarm for +itself and the alarm has timed out rather than the background process +being posted by another process to do some work.'); + + $pmda->add_metric(pmda_pmid(0,20), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.sepgamem', 'Session PGA memory', +'The "session PGA memory" statistic from the V$SYSSTAT view. This +shows the current session PGA (Process Global Area) memory size.'); + + $pmda->add_metric(pmda_pmid(0,21), PM_TYPE_U32, $sid_indom, + PM_SEM_DISCRETE, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.session.maxpgamem', 'Maximum session PGA memory', +'The "session PGA memory max" statistic from the V$SYSSTAT view. This +shows the maximum session PGA (Process Global Area) memory size.'); + + $pmda->add_metric(pmda_pmid(0,22), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.enqueue.timeouts', 'Total enqueue timeouts', +'The "enqueue timeouts" statistic from the V$SYSSTAT view. This is +the total number of enqueue operations (get and convert) that timed +out before they could complete.'); + + $pmda->add_metric(pmda_pmid(0,23), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.enqueue.waits', 'Total enqueue waits', +'The "enqueue waits" statistic from the V$SYSSTAT view. This is the +total number of waits that happened during an enqueue convert or get +because the enqueue could not be immediately granted.'); + + $pmda->add_metric(pmda_pmid(0,24), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.enqueue.deadlocks', 'Total enqueue deadlocks', +'The "enqueue deadlocks" statistic from the V$SYSSTAT view. This is +the total number of enqueue deadlocks between different sessions.'); + + $pmda->add_metric(pmda_pmid(0,25), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.enqueue.requests', 'Total enqueue requests', +'The "enqueue requests" statistic from the V$SYSSTAT view. This is +the total number of enqueue gets.'); + + $pmda->add_metric(pmda_pmid(0,26), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.enqueue.conversions', 'Total enqueue conversions', +'The "enqueue conversions" statistic from the V$SYSSTAT view. This is +the total number of enqueue converts.'); + + $pmda->add_metric(pmda_pmid(0,27), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.enqueue.releases', 'Total enqueue releases', +'The "enqueue releases" statistic from the V$SYSSTAT view. This is +the total number of enqueue releases.'); + + $pmda->add_metric(pmda_pmid(0,28), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globlock.gets', 'Total global lock gets (sync)', +'The "global lock gets (sync)" statistic from the V$SYSSTAT view. This +is the total number of synchronous global lock gets.'); + + $pmda->add_metric(pmda_pmid(0,29), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globlock.agets', 'Total global lock gets (async)', +'The "global lock gets (async)" statistic from the V$SYSSTAT view. +This is the total number of asynchronous global lock gets.'); + + $pmda->add_metric(pmda_pmid(0,30), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.globlock.gettime', 'Total global lock get time', +'The "global lock get time" statistic from the V$SYSSTAT view. This is +the total elapsed time of all synchronous global lock gets.'); + + $pmda->add_metric(pmda_pmid(0,31), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globlock.cvts', 'Total global lock converts (sync)', +'The "global lock converts (sync)" statistic from the V$SYSSTAT view. +This is the total number of synchronous global lock converts.'); + + $pmda->add_metric(pmda_pmid(0,32), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globlock.acvts', 'Total global lock converts (async)', +'The "global lock converts (async)" statistic from the V$SYSSTAT view. +This is the total number of asynchronous global lock converts.'); + + $pmda->add_metric(pmda_pmid(0,33), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.globlock.cvttime', 'Total global lock convert time', +'The "global lock convert time" statistic from the V$SYSSTAT view. +This is the total elapsed time of all synchronous global lock converts.'); + + $pmda->add_metric(pmda_pmid(0,34), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globlock.rels', 'Total global lock releases (sync)', +'The "global lock releases (sync)" statistic from the V$SYSSTAT view. +This is the total number of synchronous global lock releases.'); + + $pmda->add_metric(pmda_pmid(0,35), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globlock.arels', 'Total global lock releases (async)', +'The "global lock releases (async)" statistic from the V$SYSSTAT view. +This is the total number of asynchronous global lock releases.'); + + $pmda->add_metric(pmda_pmid(0,36), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.globlock.reltime', 'Total global lock release time', +'The "global lock release time" statistic from the V$SYSSTAT view. +This is the elapsed time of all synchronous global lock releases.'); + + $pmda->add_metric(pmda_pmid(0,37), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbbgets', 'Total db block gets', +'The "db block gets" statistic from the V$SYSSTAT view. This tracks +the number of blocks obtained in CURRENT mode.'); + + $pmda->add_metric(pmda_pmid(0,38), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.consgets', 'Total consistent gets', +'The "consistent gets" statistic from the V$SYSSTAT view. This is the +number of times a consistent read was requested for a block. Also see +the help text for oracle.sysstat.conschanges.'); + + $pmda->add_metric(pmda_pmid(0,39), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.preads', 'Total physical reads', +'The "physical reads" statistic from the V$SYSSTAT view. This is the +number of I/O requests to the operating system to retrieve a database +block from the disk subsystem. This is a buffer cache miss. +Logical reads = oracle.sysstat.consgets + oracle.sysstat.dbbgets. +Logical reads and physical reads are used to calculate the buffer hit +ratio.'); + + $pmda->add_metric(pmda_pmid(0,40), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.pwrites', 'Total physical writes', +'The "physical writes" statistic from the V$SYSSTAT view. This is the +number of I/O requests to the operating system to write a database +block to the disk subsystem. The bulk of the writes are performed +either by DBWR or LGWR.'); + + $pmda->add_metric(pmda_pmid(0,41), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.wreqs', 'Total write requests', +'The "write requests" statistic from the V$SYSSTAT view. This is the +number of times DBWR has flushed sets of dirty buffers to disk.'); + + $pmda->add_metric(pmda_pmid(0,42), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'oracle.sysstat.dirtyqlen', 'Total summed dirty queue length', +'The "summed dirty queue length" statistic from the V$SYSSTAT view. +This is the sum of the dirty LRU queue length after every write +request. +Divide by the write requests (oracle.sysstat.wreqs) to get the +average queue length after write completion. For more information see +the help text associated with oracle.sysstat.wreqs.'); + + $pmda->add_metric(pmda_pmid(0,43), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbbchanges', 'Total db block changes', +'The "db block changes" statistic from the V$SYSSTAT view. This metric +is closely related to "consistent changes" +(oracle.sysstat.conschanges) and counts the total number of +changes made to all blocks in the SGA that were part of an update or +delete operation. These are the changes that are generating redo log +entries and hence will be permanent changes to the database if the +transaction is committed. +This metric is a rough indication of total database work and indicates +(possibly on a per-transaction level) the rate at which buffers are +being dirtied.'); + + $pmda->add_metric(pmda_pmid(0,44), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.chwrtime', 'Total change write time', +'The "change write time" statistic from the V$SYSSTAT view. This is +the elapsed time for redo write for changes made to CURRENT blocks.'); + + $pmda->add_metric(pmda_pmid(0,45), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.conschanges', 'Total consistent changes', +'The "consistent changes" statistic from the V$SYSSTAT view. This is +the number of times a database block has applied rollback entries to +perform a consistent read on the block.'); + + $pmda->add_metric(pmda_pmid(0,46), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.syncwr', 'Total redo sync writes', +'The "redo sync writes" statistic from the V$SYSSTAT view. Usually, +redo that is generated and copied into the log buffer need not be +flushed out to disk immediately. The log buffer is a circular buffer +that LGWR periodically flushes. This metric is incremented when +changes being applied must be written out to disk due to commit.'); + + $pmda->add_metric(pmda_pmid(0,47), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.redo.synctime', 'Total redo sync time', +'The "redo sync time" statistic from the V$SYSSTAT view. This is the +elapsed time of all redo sync writes (oracle.sysstat.redo.syncwr).'); + +$pmda->add_metric(pmda_pmid(0,48), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.exdeadlocks', 'Total exchange deadlocks', +'The "exchange deadlocks" statistic from the V$SYSSTAT view. This is +the number of times that a process detected a potential deadlock when +exchanging two buffers and raised an internal, restartable error. +Index scans are currently the only operations which perform exchanges.'); + +$pmda->add_metric(pmda_pmid(0,49), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.buffer.freereqs', 'Total free buffer requested', +'The "free buffer requested" statistic from the V$SYSSTAT view. This is +the number of times a reusable buffer or a free buffer was requested to +create or load a block.'); + + $pmda->add_metric(pmda_pmid(0,50), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.buffer.dirtyinsp', 'Total dirty buffers inspected', +'The "dirty buffers inspected" statistic from the V$SYSSTAT view. +This is the number of dirty buffers found by the foreground while +the foreground is looking for a buffer to reuse.'); + + $pmda->add_metric(pmda_pmid(0,51), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.buffer.freeinsp', 'Total free buffer inspected', +'The "free buffer inspected" statistic from the V$SYSSTAT view. This is +the number of buffers skipped over from the end of an LRU queue in +order to find a reusable buffer. The difference between this metric +and the oracle.sysstat.buffer.dirtyinsp metric is the number of +buffers that could not be used because they were either busy, needed to +be written after rapid aging out, or they have a user, a waiter, or are +being read/written. Refer to the oracle.sysstat.buffer.dirtyinsp +help text also.'); + + $pmda->add_metric(pmda_pmid(0,52), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.timeouts', 'Total DBWR timeouts', +'The "DBWR timeouts" statistic from the V$SYSSTAT view. This is the +number of times that the DBWR has been idle since the last timeout. +These are the times that the DBWR looked for buffers to idle write.'); + + $pmda->add_metric(pmda_pmid(0,53), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.mkfreereqs', 'Total DBWR make free requests', +'The "DBWR make free requests" statistic from the V$SYSSTAT view. +This is the number of messages received requesting DBWR to make +some more free buffers for the LRU.'); + + $pmda->add_metric(pmda_pmid(0,54), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.freebuffnd', 'Total DBWR free buffers found', +'The "DBWR free buffers found" statistic from the V$SYSSTAT view. +This is the number of buffers that DBWR found to be clean when it +was requested to make free buffers. Divide this by +oracle.sysstat.dbwr.mkfreereqs to find the average number of +reusable buffers at the end of each LRU.'); + + $pmda->add_metric(pmda_pmid(0,55), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.lruscans', 'Total DBWR lru scans', +'The "DBWR lru scans" statistic from the V$SYSSTAT view. This is the +number of times that DBWR does a scan of the LRU queue looking for +buffers to write. This includes times when the scan is to fill a batch +being written for another purpose such as a checkpoint. This metric\'s +value is always greater than oracle.sysstat.dbwr.mkfreereqs.'); + + $pmda->add_metric(pmda_pmid(0,56), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.sumscandepth', 'Total DBWR summed scan depth', +'The "DBWR summed scan depth" statistic from the V$SYSSTAT view. The +current scan depth (number of buffers scanned by DBWR) is added to this +metric every time DBWR scans the LRU for dirty buffers. Divide by +oracle.sysstat.dbwr.lruscans to find the average scan depth.'); + + $pmda->add_metric(pmda_pmid(0,57), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.bufsscanned', 'Total DBWR buffers scanned', +'The "DBWR buffers scanned" statistic from the V$SYSSTAT view. +This is the total number of buffers looked at when scanning each +LRU set for dirty buffers to clean. This count includes both dirty +and clean buffers. Divide by oracle.sysstat.dbwr.lruscans to +find the average number of buffers scanned.'); + + $pmda->add_metric(pmda_pmid(0,58), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.checkpoints', 'Total DBWR checkpoints', +'The "DBWR checkpoints" statistic from the V$SYSSTAT view. +This is the number of times the DBWR was asked to scan the cache +and write all blocks marked for a checkpoint.'); + + $pmda->add_metric(pmda_pmid(0,59), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.xinstwrites', 'Total DBWR cross instance writes', +'The "DBWR cross instance writes" statistic from the V$SYSSTAT view. +This is the total number of blocks written for other instances so that +they can access the buffers.'); + + $pmda->add_metric(pmda_pmid(0,60), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.remote.instundowr', + 'Total remote instance undo writes', +'The "remote instance undo writes" statistic from the V$SYSSTAT view. +This is the number of times this instance performed a dirty undo write +so that another instance could read that data.'); + + $pmda->add_metric(pmda_pmid(0,61), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.remote.instundoreq', + 'Total remote instance undo requests', +'The "remote instance undo requests" statistic from the V$SYSSTAT view. +This is the number of times this instance requested undo from another +instance so it could be read CR.'); + + $pmda->add_metric(pmda_pmid(0,62), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.xinstcrrd', 'Total cross instance CR read', +'The "cross instance CR read" statistic from the V$SYSSTAT view. This +is the number of times this instance made a cross instance call to +write a particular block due to timeout on an instance lock get. The +call allowed the blocks to be read CR rather than CURRENT.'); + + $pmda->add_metric(pmda_pmid(0,63), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmg.cscalls', 'Total calls to kcmgcs', +'The "calls to kcmgcs" statistic from the V$SYSSTAT view. This is the +total number of calls to get the current System Commit Number (SCN).'); + +$pmda->add_metric(pmda_pmid(0,64), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmg.rscalls', 'Total calls to kcmgrs', +'The "calls to kcmgrs" statistic from the V$SYSSTAT view. This is the +total number of calls to get a recent System Commit Number (SCN).'); + + $pmda->add_metric(pmda_pmid(0,65), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmg.ascalls', 'Total calls to kcmgas', +'The "calls to kcmgas" statistic from the V$SYSSTAT view. This is the +total number of calls that Get and Advance the System Commit Number +(SCN). Also used when getting a Batch of SCN numbers.'); + + $pmda->add_metric(pmda_pmid(0,66), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.nodlmscnsgets', + 'Total next scns gotten without going to DLM', +'The "next scns gotten without going to DLM" statistic from the +V$SYSSTAT view. This is the number of SCNs (System Commit Numbers) +obtained without going to the DLM (Distributed Lock Manager).'); + + $pmda->add_metric(pmda_pmid(0,67), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.entries', 'Total redo entries', +'The "redo entries" statistic from the V$SYSSTAT view. This metric +is incremented each time redo entries are copied into the redo log +buffer.'); + + $pmda->add_metric(pmda_pmid(0,68), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.redo.size', 'Total redo size', +'The "redo size" statistic from the V$SYSSTAT view. +This is the number of bytes of redo generated.'); + + $pmda->add_metric(pmda_pmid(0,69), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.entslin', 'Total redo entries linearized', +'The "redo entries linearized" statistic from the V$SYSSTAT view. This +is the number of entries of size <= REDO_ENTRY_PREBUILD_THRESHOLD. +Building these entries increases CPU time but may increase concurrency.'); + + $pmda->add_metric(pmda_pmid(0,70), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.bufallret', + 'Total redo buffer allocation retries', +'The "redo buffer allocation retries" statistic from the V$SYSSTAT +view. This is the total number of retries necessary to allocate space +in the redo buffer. Retries are needed because either the redo writer +has gotten behind, or because an event (such as log switch) is +occuring.'); + + $pmda->add_metric(pmda_pmid(0,71), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.smallcpys', 'Total redo small copies', +'The "redo small copies" statistic from the V$SYSSTAT view. This is the +total number of entries where size <= LOG_SMALL_ENTRY_MAX_SIZE. These +entries are copied using the protection of the allocation latch, +eliminating the overhead of getting the copy latch. This is generally +only useful for multi-processor systems.'); + + $pmda->add_metric(pmda_pmid(0,72), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.redo.wastage', 'Total redo wastage', +'The "redo wastage" statistic from the V$SYSSTAT view. This is the +number of bytes wasted because redo blocks needed to be written before +they are completely full. Early writing may be needed to commit +transactions, to be able to write a database buffer or to switch logs.'); + + $pmda->add_metric(pmda_pmid(0,73), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.redo.wrlatchtime', 'Total redo writer latching time', +'The "redo writer latching time" statistic from the V$SYSSTAT view. +This is the elapsed time needed by LGWR to obtain and release each copy +latch. This is only used if the LOG_SIMULTANEOUS_COPIES initialization +parameter is greater than zero.'); + + $pmda->add_metric(pmda_pmid(0,74), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.writes', 'Total redo writes', +'The "redo writes" statistic from the V$SYSSTAT view. +This is the total number of writes by LGWR to the redo log files.'); + + $pmda->add_metric(pmda_pmid(0,75), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.bwrites', 'Total redo blocks written', +'The "redo blocks written" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,76), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.redo.wrtime', 'Total redo write time', +'The "redo write time" statistic from the V$SYSSTAT view. This is the +total elapsed time of the write from the redo log buffer to the current +redo log file.'); + + $pmda->add_metric(pmda_pmid(0,77), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.logspreqs', 'Total redo log space requests', +'The "redo log space requests" statistic from the V$SYSSTAT view. The +active log file is full and Oracle is waiting for disk space to be +allocated for the redo log entries. Space is created by performing a +log switch. +Small log files in relation to the size of the SGA or the commit rate +of the work load can cause problems. When the log switch occurs, +Oracle must ensure that all committed dirty buffers are written to disk +before switching to a new log file. If you have a large SGA full of +dirty buffers and small redo log files, a log switch must wait for DBWR +to write dirty buffers to disk before continuing.'); + + $pmda->add_metric(pmda_pmid(0,78), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.redo.logspwaittime', 'Total redo log space wait time', +'The "redo log space wait time" statistic from the V$SYSSTAT view. This +is the total elapsed time spent waiting for redo log space requests +(refer to the oracle.sysstat.redo.logspreqs metric).'); + + $pmda->add_metric(pmda_pmid(0,79), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.logswintrs', 'Total redo log switch interrupts', +'The "redo log switch interrupts" statistic from the V$SYSSTAT view. +This is the number of times that another instance asked this instance +to advance to the next log file.'); + + $pmda->add_metric(pmda_pmid(0,80), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.redo.ordermarks', 'Total redo ordering marks', +'The "redo ordering marks" statistic from the V$SYSSTAT view. This is +the number of times that an SCN (System Commit Number) had to be +allocated to force a redo record to have a higher SCN than a record +generated in another thread using the same block.'); + + $pmda->add_metric(pmda_pmid(0,81), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.hashlwgets', 'Total hash latch wait gets', +'The "hash latch wait gets" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,82), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.bgchkpts.started', + 'Total background checkpoints started', +'The "background checkpoints started" statistic from the V$SYSSTAT +view. This is the number of checkpoints started by the background. It +can be larger than the number completed if a new checkpoint overrides +an incomplete checkpoint. This only includes checkpoints of the +thread, not individual file checkpoints for operations such as offline +or begin backup. This statistic does not include the checkpoints +performed in the foreground, such as ALTER SYSTEM CHECKPOINT LOCAL.'); + + $pmda->add_metric(pmda_pmid(0,83), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.bgchkpts.completed', + 'Total background checkpoints completed', +'The "background checkpoints completed" statistic from the V$SYSSTAT +view. This is the number of checkpoints completed by the background. +This statistic is incremented when the background successfully advances +the thread checkpoint.'); + + $pmda->add_metric(pmda_pmid(0,84), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.tranlock.fgreqs', + 'Total transaction lock foreground requests', +'The "transaction lock foreground requests" statistic from the V$SYSSTAT +view. For parallel server this is incremented on each call to ktugil() +"Kernel Transaction Get Instance Lock". For single instance this has +no meaning.'); + + $pmda->add_metric(pmda_pmid(0,85), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.tranlock.fgwaittime', + 'Total transaction lock foreground wait time', +'The "transaction lock foreground wait time" statistic from the +V$SYSSTAT view. This is the total time spent waiting for a transaction +instance lock.'); + + $pmda->add_metric(pmda_pmid(0,86), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.tranlock.bggets', + 'Total transaction lock background gets', +'The "transaction lock background gets" statistic from the V$SYSSTAT +view. For parallel server this is incremented on each call to ktuglb() +"Kernel Transaction Get lock in Background".'); + + $pmda->add_metric(pmda_pmid(0,87), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.tranlock.bggettime', + 'Total transaction lock background get time', +'The "transaction lock background get time" statistic from the V$SYSSTAT +view. Total time spent waiting for a transaction instance lock in +Background.'); + + $pmda->add_metric(pmda_pmid(0,88), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.shortscans', 'Total table scans (short tables)', +'The "table scans (short tables)" statistic from the V$SYSSTAT view. +Long (or conversely short) tables can be defined by optimizer hints +coming down into the row source access layer of Oracle. The table must +have the CACHE option set.'); + + $pmda->add_metric(pmda_pmid(0,89), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.longscans', 'Total table scans (long tables)', +'The "table scans (long tables)" statistic from the V$SYSSTAT view. +Long (or conversely short) tables can be defined as tables that do not +meet the short table criteria described in the help text for the +oracle.sysstat.table.shortscans metric.'); + + $pmda->add_metric(pmda_pmid(0,90), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.scanrows', 'Total table scan rows gotten', +'The "table scan rows gotten" statistic from the V$SYSSTAT view. This +is collected during a scan operation, but instead of counting the +number of database blocks (see oracle.sysstat.table.scanblocks), +it counts the rows being processed.'); + + $pmda->add_metric(pmda_pmid(0,91), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.scanblocks', 'Total table scan blocks gotten', +'The "table scan blocks gotten" statistic from the V$SYSSTAT view. +During scanning operations, each row is retrieved sequentially by +Oracle. This metric is incremented for each block encountered during +the scan. +This informs you of the number of database blocks that you had to get +from the buffer cache for the purpose of scanning. Compare the value +of this parameter to the value of oracle.sysstat.consgets +(consistent gets) to get a feel for how much of the consistent read +activity can be attributed to scanning.'); + + $pmda->add_metric(pmda_pmid(0,92), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.rowidfetches', 'Total table fetch by rowid', +'The "table fetch by rowid" statistic from the V$SYSSTAT view. When +rows are fetched using a ROWID (usually from an index), each row +returned increments this counter. +This metric is an indication of row fetch operations being performed +with the aid of an index. Because doing table scans usually indicates +either non-optimal queries or tables without indices, this metric +should increase as the above issues have been addressed in the +application.'); + + $pmda->add_metric(pmda_pmid(0,93), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.contfetches', 'Total table fetch continued row', +'The "table fetch continued row" statistic from the V$SYSSTAT view. +This metric is incremented when a row that spans more than one block is +encountered during a fetch. +Retrieving rows that span more than one block increases the logical I/O +by a factor that corresponds to the number of blocks that need to be +accessed. Exporting and re-importing may eliminate this problem. Also +take a closer look at the STORAGE parameters PCT_FREE and PCT_USED. +This problem cannot be fixed if rows are larger than database blocks +(for example, if the LONG datatype is used and the rows are extremely +large).'); + + $pmda->add_metric(pmda_pmid(0,94), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.clustkey.scans', 'Total cluster key scans', +'The "cluster key scans" statistic from the V$SYSSTAT view. +This is the number of cluster scans that were started.'); + + $pmda->add_metric(pmda_pmid(0,95), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.clustkey.scanblocks', + 'Total cluster key scan block gets', +'The "cluster key scan block gets" statistic from the V$SYSSTAT view. +This is the number of blocks obtained in a cluster scan.'); + + $pmda->add_metric(pmda_pmid(0,96), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.sql.parsecpu', 'Total parse time cpu', +'The "parse time cpu" statistic from the V$SYSSTAT view. This is the +total CPU time used for parsing (hard and soft parsing). Units are +milliseconds of CPU time.'); + + $pmda->add_metric(pmda_pmid(0,97), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.sql.parsereal', 'Total parse time elapsed', +'The "parse time elapsed" statistic from the V$SYSSTAT view. +This is the total elapsed time for parsing. Subtracting +oracle.sysstat.sql.parsecpu from this metric gives the total +waiting time for parse resources. Units are milliseconds.'); + + $pmda->add_metric(pmda_pmid(0,98), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sql.parsed', 'Total parse count', +'The "parse count (total)" statistic from the V$SYSSTAT view. This is +the total number of parse calls (hard and soft). A soft parse is a +check to make sure that the permissions on the underlying objects have +not changed.'); + + $pmda->add_metric(pmda_pmid(0,99), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sql.executed', 'Total execute count', +'The "execute count" statistic from the V$SYSSTAT view. +This is the total number of calls (user and recursive) that +execute SQL statements.'); + + $pmda->add_metric(pmda_pmid(0,100), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sql.memsorts', 'Total sorts (memory)', +'The "sorts (memory)" statistic from the V$SYSSTAT view. If the number +of disk writes is zero, then the sort was performed completely in +memory and this metric is incremented. +This is more an indication of sorting activity in the application +workload. You cannot do much better than memory sorts, except for no +sorts at all. Sorting is usually caused by selection criteria +specifications within table join SQL operations.'); + + $pmda->add_metric(pmda_pmid(0,101), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sql.disksorts', 'Total sorts (disk)', +'The "sorts (disk)" statistic from the V$SYSSTAT view. If the number +of disk writes is non-zero for a given sort operation, then this metric +is incremented. +Sorts that require I/O to disk are quite resource intensive. +Try increasing the size of the Oracle initialization parameter +SORT_AREA_SIZE.'); + + $pmda->add_metric(pmda_pmid(0,102), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sql.rowsorts', 'Total sorts (rows)', +'The "sorts (rows)" statistic from the V$SYSSTAT view. +This is the total number of rows sorted.'); + + $pmda->add_metric(pmda_pmid(0,103), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sccachehits', 'Total session cursor cache hits', +'The "session cursor cache hits" statistic from the V$SYSSTAT view. +This is the count of the number of hits in the session cursor cache. +A hit means that the SQL statement did not have to be reparsed. +By subtracting this metric from oracle.sysstat.sql.parsed one can +determine the real number of parses that have been performed.'); + + $pmda->add_metric(pmda_pmid(0,104), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cursauths', 'Total cursor authentications', +'The "cursor authentications" statistic from the V$SYSSTAT view. This +is the total number of cursor authentications. The number of times +that cursor privileges have been verified, either for a SELECT or +because privileges were revoked from an object, causing all users of +the cursor to be re-authenticated.'); + + $pmda->add_metric(pmda_pmid(0,105), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.recovery.breads', 'Total recovery blocks read', +'The "recovery blocks read" statistic from the V$SYSSTAT view. +This is the number of blocks read during recovery.'); + + $pmda->add_metric(pmda_pmid(0,106), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.recovery.areads', 'Total recovery array reads', +'The "recovery array reads" statistic from the V$SYSSTAT view. This is +the number of reads performed during recovery.'); + + $pmda->add_metric(pmda_pmid(0,107), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.recovery.areadtime', 'Total recovery array read time', +'The "recovery array read time" statistic from the V$SYSSTAT view. +This is the elapsed time of I/O while doing recovery.'); + + $pmda->add_metric(pmda_pmid(0,108), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.rowidrngscans', + 'Total table scans (rowid ranges)', +'The "table scans (rowid ranges)" statistic from the V$SYSSTAT view. +This is a count of the table scans with specified ROWID endpoints. +These scans are performed for Parallel Query.'); + + $pmda->add_metric(pmda_pmid(0,109), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.cachepartscans', + 'Total table scans (cache partitions)', +'The "table scans (cache partitions)" statistic from the V$SYSSTAT +view. This is a count of range scans on tables that have the CACHE +option enabled.'); + + $pmda->add_metric(pmda_pmid(0,110), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cr.createblk', 'Total CR blocks created', +'The "CR blocks created" statistic from the V$SYSSTAT view. +A buffer in the buffer cache was cloned. The most common reason +for cloning is that the buffer is held in an incompatible mode.'); + + $pmda->add_metric(pmda_pmid(0,111), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cr.convcurrblk', + 'Total Current blocks converted for CR', +'The "Current blocks converted for CR" statistic from the V$SYSSTAT +view. A CURRENT buffer (shared or exclusive) is made CR before it can +be used.'); + + $pmda->add_metric(pmda_pmid(0,112), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.unnecprocclnscn', + 'Total Unnecessary process cleanup for SCN batching', +'The "Unnecessary process cleanup for SCN batching" statistic from the +V$SYSSTAT view. This is the total number of times that the process +cleanup was performed unnecessarily because the session/process did not +get the next batched SCN (System Commit Number). The next batched SCN +went to another session instead.'); + + $pmda->add_metric(pmda_pmid(0,113), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.consread.transtable.undo', + 'Total transaction tables consistent reads - undo records applied', +'The "transaction tables consistent reads - undo records applied" +statistic from the V$SYSSTAT view. This is the number of UNDO records +applied to get CR images of data blocks.'); + + $pmda->add_metric(pmda_pmid(0,114), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.consread.transtable.rollback', + 'Total transaction tables consistent read rollbacks', +'The "transaction tables consistent read rollbacks" statistic from the +V$SYSSTAT view. This is the total number of times transaction tables +are CR rolled back.'); + + $pmda->add_metric(pmda_pmid(0,115), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.datablkundo', + 'Total data blocks consistent reads - undo records applied', +'The "data blocks consistent reads - undo records applied" statistic +from the V$SYSSTAT view. This is the total number of UNDO records +applied to get CR images of data blocks.'); + + $pmda->add_metric(pmda_pmid(0,116), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.noworkgets', 'Total no work - consistent read gets', +'The "no work - consistent read gets" statistic from the V$SYSSTAT +view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,117), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.consread.cleangets', + 'Total cleanouts only - consistent read gets', +'The "cleanouts only - consistent read gets" statistic from the +V$SYSSTAT view. The number of times a CR get required a block +cleanout ONLY and no application of undo.'); + + $pmda->add_metric(pmda_pmid(0,118), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.consread.rollbackgets', + 'Total rollbacks only - consistent read gets', +'The "rollbacks only - consistent read gets" statistic from the +V$SYSSTAT view. This is the total number of CR operations requiring +UNDO to be applied but no block cleanout.'); + + $pmda->add_metric(pmda_pmid(0,119), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.consread.cleanrollbackgets', + 'Total cleanouts and rollbacks - consistent read gets', +'The "cleanouts and rollbacks - consistent read gets" statistic from the +V$SYSSTAT view. This is the total number of CR gets requiring BOTH +block cleanout and subsequent rollback to get to the required snapshot +time.'); + + $pmda->add_metric(pmda_pmid(0,120), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.rollbackchangeundo', + 'Total rollback changes - undo records applied', +'The "rollback changes - undo records applied" statistic from the +V$SYSSTAT view. This is the total number of undo records applied to +blocks to rollback real changes. Eg: as a result of a rollback command +and *NOT* in the process of getting a CR block image. +Eg: commit; + insert into mytab values (10); + insert into mytab values (20); + rollback; +should increase this statistic by 2 (assuming no recursive operations).'); + + $pmda->add_metric(pmda_pmid(0,121), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.transrollbacks', 'Total transaction rollbacks', +'The "transaction rollbacks" statistic from the V$SYSSTAT view. This is +the actual transaction rollbacks that involve undoing real changes. +Contrast with oracle.sysstat.user_rollbacks which only indicates the +number of ROLLBACK statements received.'); + + $pmda->add_metric(pmda_pmid(0,122), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cleanout.immedcurr', + 'Total immediate (CURRENT) block cleanout applications', +'The "immediate (CURRENT) block cleanout applications" statistic from +the V$SYSSTAT view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,123), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cleanout.immedcr', + 'Total immediate (CR) block cleanout applications', +'The "immediate (CR) block cleanout applications" statistic from the +V$SYSSTAT view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,124), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cleanout.defercurr', + 'Total deferred (CURRENT) block cleanout applications', +'The "deferred (CURRENT) block cleanout applications" statistic from +the V$SYSSTAT view. This is the number of times cleanout records are +deferred. Deferred changes are piggybacked with real changes.'); + + $pmda->add_metric(pmda_pmid(0,125), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.table.dirreadscans', 'Total table scans (direct read)', +'The "table scans (direct read)" statistic from the V$SYSSTAT view. +This is a count of table scans performed with direct read (bypassing +the buffer cache).'); + + $pmda->add_metric(pmda_pmid(0,126), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sccachecount', 'Total session cursor cache count', +'The "session cursor cache count" statistic from the V$SYSSTAT view. +This is the total number of cursors cached. This is only incremented +if SESSION_CACHED_CURSORS is greater than zero. This metric is the +most useful in V$SESSTAT. If the value for this statistic is close to +the setting of the initialization parameter SESSION_CACHED_CURSORS, the +value of the initialization parameter should be increased.'); + + $pmda->add_metric(pmda_pmid(0,127), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.totalfileopens', 'Total file opens', +'The "total file opens" statistic from the V$SYSSTAT view. This is the +total number of file opens being performed by the instance. Each +process needs a number of files (control file, log file, database file) +in order to work against the database.'); + + $pmda->add_metric(pmda_pmid(0,128), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cachereplaceopens', + 'Opens requiring cache replacement', +'The "opens requiring cache replacement" statistic from the V$SYSSTAT +view. This is the total number of file opens that caused a current +file to be closed in the process file cache.'); + + $pmda->add_metric(pmda_pmid(0,129), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.replacedfileopens', 'Opens of replaced files', +'The "opens of replaced files" statistic from the V$SYSSTAT view. This +is the total number of files that needed to be reopened because they +were no longer in the process file cache.'); + + $pmda->add_metric(pmda_pmid(0,130), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.total', 'Total commit cleanout calls', +'The "commit cleanouts" statistic from the V$SYSSTAT view. This is the +number of times that the cleanout block at commit time function was +performed.'); + + $pmda->add_metric(pmda_pmid(0,131), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.completed', + 'Successful commit cleanouts', +'The "commit cleanouts successfully completed" metric from the V$SYSSTAT +view. This is the number of times the cleanout block at commit time +function successfully completed.'); + + $pmda->add_metric(pmda_pmid(0,132), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.failures.writedisabled', + 'Commits when writes disabled', +'The "commit cleanout failures: write disabled" statistic from the +V$SYSSTAT view. This is the number of times that a cleanout at commit +time was performed but the writes to the database had been temporarily +disabled.'); + + $pmda->add_metric(pmda_pmid(0,133), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.failures.hotbackup', + 'Commit attempts during hot backup', +'The "commit cleanout failures: hot backup in progress" statistic +from the V$SYSSTAT view. This is the number of times that cleanout +at commit was attempted during hot backup.'); + + $pmda->add_metric(pmda_pmid(0,134), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.failures.bufferwrite', + 'Commits while buffer being written', +'The "commit cleanout failures: buffer being written" statistic from the +V$SYSSTAT view. This is the number of times that a cleanout at commit +time was attempted but the buffer was being written at the time.'); + + $pmda->add_metric(pmda_pmid(0,135), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.failures.callbackfail', + 'Commit callback fails', +'The "commit cleanout failures: callback failure" statistic from the +V$SYSSTAT view. This is the number of times that the cleanout callback +function returned FALSE (failed).'); + + $pmda->add_metric(pmda_pmid(0,136), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.failures.blocklost', + 'Commit fails due to lost block', +'The "commit cleanout failures: block lost" statistic from the V$SYSSTAT +view. This is the number of times that a cleanout at commit was +attempted but could not find the correct block due to forced write, +replacement, or switch CURRENT.'); + + $pmda->add_metric(pmda_pmid(0,137), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitcleanouts.failures.cannotpin', + 'Commit fails due to block pinning', +'The "commit cleanout failures: cannot pin" statistic from the V$SYSSTAT +view. This is the number of times that a commit cleanout was performed +but failed because the block could not be pinned.'); + + $pmda->add_metric(pmda_pmid(0,138), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.skiphotwrites', 'Total DBWR hot writes skipped', +'The "DBWR skip hot writes" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,139), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.ckptbufwrites', + 'Total DBWR checkpoint buffers written', +'The "DBWR checkpoint buffers written" statistic from the V$SYSSTAT +view. This is the number of times the DBWR was asked to scan the cache +and write all blocks marked for checkpoint.'); + + $pmda->add_metric(pmda_pmid(0,140), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.transwrites', + 'Total DBWR transaction table writes', +'The "DBWR transaction table writes" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,141), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.undoblockwrites', 'Total DBWR undo block writes', +'The "DBWR undo block writes" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,142), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.ckptwritereq', + 'Total DBWR checkpoint write requests', +'The "DBWR checkpoint write requests" statistic from the V$SYSSTAT +view. This is the number of times the DBWR was asked to scan the cache +and write all blocks.'); + + $pmda->add_metric(pmda_pmid(0,143), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.incrckptwritereq', + 'Total DBWR incr checkpoint write requests', +'The "DBWR incr. ckpt. write requests" statistic from the V$SYSSTAT +view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,144), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.revisitbuf', + 'Total DBWR being-written buffer revisits', +'The "DBWR revisited being-written buffer" statistic from the V$SYSSTAT +view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,145), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.xinstflushcalls', + 'Total DBWR cross instance flush calls', +'The "DBWR Flush object cross instance calls" statistic from the +V$SYSSTAT view. This is the number of times DBWR received a flush by +object number cross instance call (from a remote instance). This +includes both checkpoint and invalidate object.'); + + $pmda->add_metric(pmda_pmid(0,146), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.dbwr.nodirtybufs', + 'DBWR flush calls finding no dirty buffers', +'The "DBWR Flush object call found no dirty buffers" statistic from the +V$SYSSTAT view. DBWR didn\'t find any dirty buffers for an object that +was flushed from the cache.'); + + $pmda->add_metric(pmda_pmid(0,147), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.remote.instundoblockwr', + 'Remote instance undo block writes', +'The "remote instance undo block writes" statistic from the V$SYSSTAT +view. This is the number of times this instance wrote a dirty undo +block so that another instance could read it.'); + + $pmda->add_metric(pmda_pmid(0,148), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.remote.instundoheaderwr', + 'Remote instance undo header writes', +'The "remote instance undo header writes" statistic from the V$SYSSTAT +view. This is the number of times this instance wrote a dirty undo +header block so that another instance could read it.'); + + $pmda->add_metric(pmda_pmid(0,149), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmgss_snapshotscn', + 'Total calls to get snapshot SCN: kcmgss', +'The "calls to get snapshot scn: kcmgss" statistic from the V$SYSSTAT +view. This is the number of times a snap System Commit Number (SCN) +was allocated. The SCN is allocated at the start of a transaction.'); + + $pmda->add_metric(pmda_pmid(0,150), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmgss_batchwait', 'Total kcmgss waits for batching', +'The "kcmgss waited for batching" statistic from the V$SYSSTAT view. +This is the number of times the kernel waited on a snapshot System +Commit Number (SCN).'); + + $pmda->add_metric(pmda_pmid(0,151), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmgss_nodlmscnread', + 'Total kcmgss SCN reads with using DLM', +'The "kcmgss read scn without going to DLM" statistic from the V$SYSSTAT +view. This is the number of times the kernel casually confirmed the +System Commit Number (SCN) without using the Distributed Lock Manager +(DLM).'); + + $pmda->add_metric(pmda_pmid(0,152), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.kcmccs_currentscn', + 'Total kcmccs calls to get current SCN', +'The "kcmccs called get current scn" statistic from the V$SYSSTAT view. +This is the number of times the kernel got the CURRENT SCN (System +Commit Number) when there was a need to casually confirm the SCN.'); + + $pmda->add_metric(pmda_pmid(0,153), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.serializableaborts', 'Total serializable aborts', +'The "serializable aborts" statistic from the V$SYSSTAT view. This is +the number of times a SQL statement in serializable isolation level had +to abort.'); + + $pmda->add_metric(pmda_pmid(0,154), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globalcache.hashlatchwaits', + 'Global cache hash latch waits', +'The "global cache hash latch waits" statistic from the V$SYSSTAT view. +This is the number of times that the buffer cache hash chain latch +couldn\'t be acquired immediately, when processing a lock element.'); + + $pmda->add_metric(pmda_pmid(0,155), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globalcache.freelistwaits', + 'Global cache freelist waits', +'The "global cache freelist waits" statistic from the V$SYSSTAT view. +This is the number of pings for free lock elements (when all release +locks are in use).'); + + $pmda->add_metric(pmda_pmid(0,156), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.globalcache.defers', + 'Global cache ping request defers', +'The "global cache defers" statistic from the V$SYSSTAT view. +This is the number of times a ping request was deferred until later.'); + + $pmda->add_metric(pmda_pmid(0,157), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.instrecoverdbfreeze', + 'Instance recovery database freezes', +'The "instance recovery database freeze count" statistic from the +V$SYSSTAT view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,158), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.commitscncached', 'Commit SCN cached', +'The "Commit SCN cached" statistic from the V$SYSSTAT view. The System +Commit Number (SCN) is used to serialize time within a single instance, +and across all instances. This lock resource caches the current value +of the SCN - the value is incremented in response to many database +events, but most notably COMMIT WORK. Access to the SCN lock value to +get and store the SCN is batched on most cluster implementations, so +that every process that needs a new SCN gets one and stores a new value +back on one instance, before the SCN lock is released so that it may be +granted to another instance. Processes get the SC lock once and then +use conversion operations to manipulate the lock value.'); + + $pmda->add_metric(pmda_pmid(0,159), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.cachedscnreferenced', 'Cached Commit SCN referenced', +'The "Cached Commit SCN referenced" statistic from the V$SYSSTAT view. +The SCN (System Commit Number), is generally a timing mechanism Oracle +uses to guarantee ordering of transactions and to enable correct +recovery from failure. They are used for guaranteeing +read-consistency, and checkpointing.'); + + $pmda->add_metric(pmda_pmid(0,160), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.hardparsed', 'Total number of hard parses performed', +'The "parse count (hard)" statistic from the V$SYSSTAT view. This is +the total number of parse calls (real parses). A hard parse means +allocating a workheap and other memory structures, and then building a +parse tree. A hard parse is a very expensive operation in terms of +memory use.'); + + $pmda->add_metric(pmda_pmid(0,161), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.sqlnet.clientrecvs', + 'Total bytes from client via SQL*Net', +'The "bytes received via SQL*Net from client" statistic from the +V$SYSSTAT view. This is the total number of bytes received from the +client over SQL*Net.'); + + $pmda->add_metric(pmda_pmid(0,162), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.sqlnet.clientsends', + 'Total bytes to client via SQL*Net', +'The "bytes sent via SQL*Net to client" statistic from the V$SYSSTAT +view. This is the total number of bytes sent to the client over +SQL*Net.'); + + $pmda->add_metric(pmda_pmid(0,163), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sqlnet.clientroundtrips', + 'Total client SQL*Net roundtrips', +'The "SQL*Net roundtrips to/from client" statistic from the V$SYSSTAT +view. This is the total number of network messages sent to and +received from the client.'); + + $pmda->add_metric(pmda_pmid(0,164), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.sqlnet.dblinkrecvs', + 'Total bytes from dblink via SQL*Net', +'The "bytes received via SQL*Net from dblink" statistic from the +V$SYSSTAT view. This is the total number of bytes received from +the database link over SQL*Net.'); + + $pmda->add_metric(pmda_pmid(0,165), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.sysstat.sqlnet.dblinksends', + 'Total bytes to dblink via SQL*Net', +'The "bytes sent via SQL*Net to dblink" statistic from the V$SYSSTAT +view. This is the total number of bytes sent over a database link.'); + + $pmda->add_metric(pmda_pmid(0,166), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.sqlnet.dblinkroundtrips', + 'Total dblink SQL*Net roundtrips', +'The "SQL*Net roundtrips to/from dblink" statistic from the V$SYSSTAT +view. This is the total number of network messages sent to and +received from a database link.'); + + $pmda->add_metric(pmda_pmid(0,167), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.parallel.queries', 'Total queries parallelized', +'The "queries parallelized" statistic from the V$SYSSTAT view. This is +the number of SELECT statements which have been parallelized.'); + + $pmda->add_metric(pmda_pmid(0,168), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.parallel.DMLstatements', + 'Total DML statements parallelized', +'The "DML statements parallelized" statistic from the V$SYSSTAT view. +This is the number of Data Manipulation Language (DML) statements which +have been parallelized.'); + + $pmda->add_metric(pmda_pmid(0,169), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.parallel.DDLstatements', + 'Total DDL statements parallelized', +'The "DDL statements parallelized" statistic from the V$SYSSTAT view. +This is the number of Data Definition Language (DDL) statements which +have been parallelized.'); + + $pmda->add_metric(pmda_pmid(0,170), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.PX.localsends', 'PX local messages sent', +'The "PX local messages sent" statistic from the V$SYSSTAT view. +This is the number of local messages sent for Parallel Execution.'); + + $pmda->add_metric(pmda_pmid(0,171), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.PX.localrecvs', 'PX local messages received', +'The "PX local messages recv\'d" statistic from the V$SYSSTAT view. +This is the number of local messages received for Parallel Execution.'); + + $pmda->add_metric(pmda_pmid(0,172), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.PX.remotesends', 'PX remote messages sent', +'The "PX remote messages sent" statistic from the V$SYSSTAT view. +This is the number of remote messages sent for Parallel Execution.'); + + $pmda->add_metric(pmda_pmid(0,173), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.PX.remoterecvs', 'PX remote messages received', +'The "PX remote messages recvd" statistic from the V$SYSSTAT view. +This is the number of remote messages received for Parallel Execution.'); + + $pmda->add_metric(pmda_pmid(0,174), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.buffer.pinned', 'Total pinned buffers', +'The "buffer is pinned count" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,175), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.buffer.notpinned', 'Total not pinned buffers', +'The "buffer is not pinned count" statistic from the V$SYSSTAT view. +This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,176), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.sysstat.buffer.nonetopin', 'No buffer to keep pinned count', +'The "no buffer to keep pinned count" statistic from the V$SYSSTAT +view. This metric is not documented by Oracle.'); + + $pmda->add_metric(pmda_pmid(0,177), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.OS.utime', 'OS User time used', +'The "OS User time used" statistic from the V$SYSSTAT view. +Units are milliseconds of CPU user time.'); + + $pmda->add_metric(pmda_pmid(0,178), PM_TYPE_U32, $sid_indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'oracle.sysstat.OS.stime', 'OS System time used', +'The "OS System time used" statistic from the V$SYSSTAT view. +Units are milliseconds of CPU system time.'); +} + +sub setup_rowcache ## row cache statistics from v$rowcache +{ + $pmda->add_metric(pmda_pmid(7,0), PM_TYPE_U32, $rowcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rowcache.count', + 'Number of entries in this data dictionary cache', +'The total number of data dictionary cache entries, broken down by data +type. This is extracted from the COUNT column of the V$ROWCACHE view.'); + + $pmda->add_metric(pmda_pmid(7,1), PM_TYPE_U32, $rowcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rowcache.gets', + 'Number of requests for cached information on data dictionary objects', +'The total number of valid data dictionary cache entries, broken down by +data type. This is extracted from the GETS column of the V$ROWCACHE +view.'); + + $pmda->add_metric(pmda_pmid(7,2), PM_TYPE_U32, $rowcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rowcache.getmisses', + 'Number of data requests resulting in cache misses', +'The total number of data dictionary requests that resulted in cache +misses, broken down by data type. This is extracted from the GETMISSES +column of the V$ROWCACHE view.'); + + $pmda->add_metric(pmda_pmid(7,3), PM_TYPE_U32, $rowcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rowcache.scans', 'Number of scan requests', +'The total number of data dictionary cache scans, broken down by data +type. This is extracted from the SCANS column of the V$ROWCACHE view.'); + + $pmda->add_metric(pmda_pmid(7,4), PM_TYPE_U32, $rowcache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rowcache.scanmisses', + 'Number of data dictionary cache misses', +'The total number of times data dictionary cache scans failed to find +data in the cache, broken down by data type. This is extracted from +the SCANMISSES column of the V$ROWCACHE view.'); +} + +sub setup_rollstat ## rollback I/O statistics from v$rollstat +{ + $pmda->add_metric(pmda_pmid(4,0), PM_TYPE_U32, $rollback_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.rollback.rssize', 'Size of rollback segment', +'Size in bytes of the rollback segment. This value is obtained from the +RSSIZE column in the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,1), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.writes', + 'Number of bytes written to rollback segment', +'The total number of bytes written to rollback segment. This value is +obtained from the WRITES column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,2), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.xacts', 'Number of active transactions', +'The number of active transactions. This value is obtained from the +XACTS column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,3), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.gets', + 'Number of header gets for rollback segment', +'The number of header gets for the rollback segment. This value is +obtained from the GETS column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,4), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.waits', + 'Number of header waits for rollback segment', +'The number of header gets for the rollback segment. This value is +obtained from the WAIT column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,5), PM_TYPE_U32, $rollback_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.rollback.hwmsize', + 'High water mark of rollback segment size', +'High water mark of rollback segment size. This value is obtained from +the HWMSIZE column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,6), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.shrinks', + 'Number of times rollback segment shrank', +'The number of times the size of the rollback segment decreased, +eliminating additional extents. This value is obtained from the +SHRINKS column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,7), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.wraps', + 'Number of times rollback segment wrapped', +'The number of times the rollback segment wrapped from one extent +to another. This value is obtained from the WRAPS column of the +V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,8), PM_TYPE_U32, $rollback_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.rollback.extends', + 'Number of times rollback segment size extended', +'The number of times the size of the rollback segment grew to include +another extent. This value is obtained from the EXTENDS column of the +V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,9), PM_TYPE_U32, $rollback_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.rollback.avshrink', 'Average shrink size', +'Average of freed extent size for rollback segment. This value is +obtained from the AVESHRINK column of the V$ROLLSTAT view.'); + + $pmda->add_metric(pmda_pmid(4,10), PM_TYPE_U32, $rollback_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_BYTE,0,0), + 'oracle.rollback.avactive', + 'Current size of active entents averaged over time', +'Current average size of extents with uncommitted transaction data. +This value is obtained from the AVEACTIVE column from the V$ROLLSTAT +view.'); +} + +sub setup_reqdist ## request time histogram from v$reqdist +{ + $pmda->add_metric(pmda_pmid(5,0), PM_TYPE_U32, $reqdist_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.reqdist', 'Histogram of database operation request times', +'A histogram of database request times divided into twelve buckets (time +ranges). This is extracted from the V$REQDIST table. +NOTE: + The TIMED_STATISTICS database parameter must be TRUE or this metric + will not return any values.'); +} + +sub setup_object_cache ## cache statistics from v$db_object_cache +{ + $pmda->add_metric(pmda_pmid(9,0), PM_TYPE_U32, $object_cache_indom, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'oracle.object_cache.sharemem', + 'Sharable memory usage in database cache pool by object types', +'The amount of sharable memory in the shared pool consumed by various +objects, divided into object types. The valid object types are: +INDEX, TABLE, CLUSTER, VIEW, SET, SYNONYM, SEQUENCE, PROCEDURE, +FUNCTION, PACKAGE, PACKAGE BODY, TRIGGER, CLASS, OBJECT, USER, DBLINK, +NON_EXISTENT, NOT LOADED and OTHER. +The values for each of these object types are obtained from the +SHARABLE_MEM column of the V$DB_OBJECT_CACHE view.'); + + $pmda->add_metric(pmda_pmid(9,1), PM_TYPE_U32, $object_cache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.object_cache.loads', 'Number of times object loaded', +'The number of times the object has been loaded. This count also +increases when and object has been invalidated. These values are +obtained from the LOADS column of the V$DB_OBJECT_CACHE view.'); + + $pmda->add_metric(pmda_pmid(9,2), PM_TYPE_U32, $object_cache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.object_cache.locks', + 'Number of users currently locking this object', +'The number of users currently locking this object. These values are +obtained from the LOCKS column of the V$DB_OBJECT_CACHE view.'); + + $pmda->add_metric(pmda_pmid(9,3), PM_TYPE_U32, $object_cache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.object_cache.pins', + 'Number of users currently pinning this object', +'The number of users currently pinning this object. These values are +obtained from the PINS column of the V$DB_OBJECT_CACHE view.'); +} + +sub setup_license ## licence data from v$license +{ + $pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.license.maxsess', + 'Maximum number of concurrent user sessions', +'The maximum number of concurrent user sessions permitted for the +instance. This value is obtained from the SESSIONS_MAX column of +the V$LICENSE view.'); + + $pmda->add_metric( + pmda_pmid(1,1), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.license.cursess', + 'Current number of concurrent user sessions', +'The current number of concurrent user sessions for the instance. +This value is obtained from the SESSIONS_CURRENT column of the +V$LICENSE view.'); + + $pmda->add_metric( + pmda_pmid(1,2), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.license.warnsess', + 'Warning limit for concurrent user sessions', +'The warning limit for concurrent user sessions for this instance. +This value is obtained from the SESSIONS_WARNING column of the +V$LICENSE view.'); + + $pmda->add_metric( + pmda_pmid(1,3), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.license.highsess', + 'Highest number of concurrent user sessions since instance started', +'The highest number of concurrent user sessions since the instance +started. This value is obtained from the SESSIONS_HIGHWATER column of +the V$LICENSE view.'); + + $pmda->add_metric(pmda_pmid(1,4), PM_TYPE_U32, $sid_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.license.maxusers', + 'Maximum number of named users permitted', +'The maximum number of named users allowed for the database. This value +is obtained from the USERS_MAX column of the V$LICENSE view.'); +} + +sub setup_librarycache ## statistics from v$librarycache +{ + $pmda->add_metric(pmda_pmid(12,0), PM_TYPE_U32, $librarycache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.gets', + 'Number of lock requests for each namespace object', +'The number of times a lock was requested for objects of this +namespace. This value is obtained from the GETS column of the +V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,1), PM_TYPE_U32, $librarycache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.gethits', + 'Number of times objects handle found in memory', +'The number of times an object\'s handle was found in memory. This value +is obtained from the GETHITS column of the V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,2), PM_TYPE_FLOAT, $librarycache_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.gethitratio', + 'Ratio of gethits to hits', +'The ratio of GETHITS to HITS. This value is obtained from the +GETHITRATIO column of the V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,3), PM_TYPE_U32, $librarycache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.pins', + 'Number of times a pin was requested for each namespace object', +'The number of times a PIN was requested for each object of the library +cache namespace. This value is obtained from the PINS column of the +V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,4), PM_TYPE_U32, $librarycache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.pinhits', + 'Number of times all metadata found in memory', +'The number of times that all of the meta data pieces of the library +object were found in memory. This value is obtained from the PINHITS +column of the V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,5), PM_TYPE_FLOAT, $librarycache_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.pinhitratio', 'Ratio of pins to pinhits', +'The ratio of PINS to PINHITS. This value is obtained from the +PINHITRATIO column of the V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,6), PM_TYPE_U32, $librarycache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.reloads', 'Number of disk reloads required', +'Any PIN of an object that is not the first PIN performed since the +object handle was created, and which requires loading the object from +the disk. This value is obtained from the RELOADS column of the +V$LIBRARYCACHE view.'); + + $pmda->add_metric(pmda_pmid(12,7), PM_TYPE_U32, $librarycache_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.librarycache.invalidations', + 'Invalidations due to dependent object modifications', +'The total number of times objects in the library cache namespace were +marked invalid due to a dependent object having been modified. This +value is obtained from the INVALIDATIONS column of the V$LIBRARYCACHE +view.'); +} + +sub setup_latch ## latch statistics from v$latch +{ + $pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.gets', + 'Number of times obtained a wait', +'The number of times latch obtained a wait. These values are obtained +from the GETS column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,1), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.misses', + 'Number of times obtained a wait but failed on first try', +'The number of times obtained a wait but failed on the first try. These +values are obtained from the MISSES column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,2), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.sleeps', + 'Number of times slept when wanted a wait', +'The number of times slept when wanted a wait. These values are +obtained from the SLEEPS column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,3), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.imgets', + 'Number of times obtained without a wait', +'The number of times latch obtained without a wait. These values are +obtained from the IMMEDIATE_GETS column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,4), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.immisses', + 'Number of times failed to get latch without a wait', +'The number of times failed to get latch without a wait. These values +are obtained from the IMMEDIATE_MISSES column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,5), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.wakes', + 'Number of times a wait was awakened', +'The number of times a wait was awakened. These values are obtained +from the WAITERS_WOKEN column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,6), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.holds', + 'Number of waits while holding a different latch', +'The number of waits while holding a different latch. These values are +obtained from the WAITS_HOLDING_LATCH column of the V$LATCH view.'); + + $pmda->add_metric(pmda_pmid(2,7), PM_TYPE_U32, $latch_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.latch.spingets', + 'Gets that missed first try but succeeded on spin', +'Gets that missed first try but succeeded on spin. These values are +obtained from the SPIN_GETS column of the V$LATCH view.'); +} + +sub setup_backup ## file backup status from v$backup +{ + $pmda->add_metric(pmda_pmid(6,0), PM_TYPE_U32, $file_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'oracle.backup.status', + 'Backup status of online datafiles', +'The Backup status of online datafiles. The status is encoded as an +ASCII character: + not active - ( 45) + active + ( 43) + offline o (111) + normal n (110) + error E ( 69) +This value is extracted from the STATUS column of the V$BACKUP view.'); +} + +sub setup_filestat ## file I/O statistics from v$filestat +{ + $pmda->add_metric(pmda_pmid(3,0), PM_TYPE_U32, $file_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.file.phyrds', + 'Physical reads from database files', +'The number of physical reads from each database file. These values +are obtained from the PHYRDS column in the V$FILESTAT view.'); + + $pmda->add_metric(pmda_pmid(3,1), PM_TYPE_U32, $file_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.file.phywrts', + 'Physical writes to database files', +'The number of times the DBWR process is required to write to each of +the database files. These values are obtained from the PHYWRTS column +in the V$FILESTAT view.'); + + $pmda->add_metric(pmda_pmid(3,2), PM_TYPE_U32, $file_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.file.phyblkrd', + 'Physical blocks read from database files', +'The number of physical blocks read from each database file. These +values are obtained from the PHYBLKRDS column in the V$FILESTAT view.'); + + $pmda->add_metric(pmda_pmid(3,3), PM_TYPE_U32, $file_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'oracle.file.phyblkwrt', + 'Physical blocks written to database files', +'The number of physical blocks written to each database file. These +values are obtained from the PHYBLKWRT column in the V$FILESTAT view.'); + + $pmda->add_metric(pmda_pmid(3,4), PM_TYPE_U32, $file_indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'oracle.file.readtim', + 'Time spent reading from database files', +'The number of milliseconds spent doing reads if the TIMED_STATISTICS +database parameter is true. If this parameter is false, then the +metric will have a value of zero. This value is obtained from the +READTIM column of the V$FILESTAT view.'); + + $pmda->add_metric(pmda_pmid(3,5), PM_TYPE_U32, $file_indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'oracle.file.writetim', + 'Time spent writing to database files', +'The number of milliseconds spent doing writes if the TIMED_STATISTICS +database parameter is true. If this parameter is false, then the +metric will have a value of zero. This value is obtained from the +WRITETIM column of the V$FILESTAT view.'); +} + +$pmda = PCP::PMDA->new('oracle', 32); +oracle_metrics_setup(); +oracle_indoms_setup(); + +$pmda->set_fetch_callback(\&oracle_fetch_callback); +$pmda->set_fetch(\&oracle_connection_setup); +$pmda->set_refresh(\&oracle_refresh); +$pmda->set_user('oracle'); +$pmda->run; + +=pod + +=head1 NAME + +pmdaoracle - performance metrics domain agent for Oracle databases + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) that obtains +performance metrics from an Oracle database instance and makes them +available to users of the Performance Co-Pilot (PCP) monitor tools. + +B retrieves information from the database by querying the +dynamic performance (V$...) views. +Queries are performed only when metrics are requested from the PMDA to +minimize impact on the database. + +B can monitor multiple Oracle database instances. +If multiple database instances are to be monitored with PCP, each must +be configured during installation. + +The Performance Metrics Collector Daemon, B launches B; +it should not be executed directly. See the installation section below +for instructions on how to configure and start the agent. + +=head1 INSTALLATION + +B uses a configuration file from (in this order): + +=over + +=item * /etc/pcpdbi.conf + +=item * $PCP_PMDAS_DIR/oracle/oracle.conf + +=back + +This file can contain overridden values (Perl code) for the settings +listed at the start of pmdaoracle.pl, namely: + +=over + +=item * database name (see DBI(3) for details) + +=item * database user name + +=item * database pass word + +=back + +Once this is setup, you can access the names and values for the +oracle performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/oracle + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/oracle + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item /etc/pcpdbi.conf + +configuration file for all PCP database monitors + +=item $PCP_VAR_DIR/config/oracle/oracle.conf + +configuration file for B + +=item $PCP_PMDAS_DIR/oracle/oracle.conf + +alternate configuration file for B + +=item $PCP_PMDAS_DIR/oracle/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/oracle/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/oracle.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), pmdadbping.pl(1) and DBI(3). diff --git a/src/pmdas/papi/GNUmakefile b/src/pmdas/papi/GNUmakefile new file mode 100644 index 0000000..d2e3774 --- /dev/null +++ b/src/pmdas/papi/GNUmakefile @@ -0,0 +1,59 @@ +# +# Copyright (c) 2012-2014 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = papi +DOMAIN = PAPI +CMDTARGET = pmda$(IAM)$(EXECSUFFIX) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMDAINIT = $(IAM)_init + +LLDLIBS = -lpapi $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) -I. + +CFILES = papi.c +DFILES = README help + +LDIRT = domain.h *.o $(IAM).log $(CMDTARGET) $(LIBTARGET) + + +default_pcp default: build-me + +include $(BUILDRULES) + +ifeq "$(ENABLE_PAPI)" "enable_papi" + +build-me: domain.h $(CMDTARGET) $(LIBTARGET) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(CMDTARGET) $(LIBTARGET) $(PMDADIR) + $(INSTALL) -m 644 root pmns domain.h $(DFILES) $(PMDADIR) +else +build-me: +install_pcp install: +endif + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +papi.o: domain.h +papi.o: $(VERSION_SCRIPT) diff --git a/src/pmdas/papi/Install b/src/pmdas/papi/Install new file mode 100644 index 0000000..aad3276 --- /dev/null +++ b/src/pmdas/papi/Install @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the PAPI PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=papi +pmda_interface=6 +forced_restart=true + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/papi/README b/src/pmdas/papi/README new file mode 100644 index 0000000..4c1cbf1 --- /dev/null +++ b/src/pmdas/papi/README @@ -0,0 +1,54 @@ +Papi PMDA +========= + +This PMDA exports Performance API (PAPI) eventsets related to memory +usage and instruction statistics. + +Metrics +======= + +The file ./help contains descriptions for all the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list +all the available metrics and their explanatory "help" text: + + $ pminfo -fT papi + +Installation +============ + + + # cd $PCP_PMDAS_DIR/papi + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + If you choose the "default" installation, appropriate values will + be assigned to those parameters that control the customiztion of + the PMDA. Otherwise consult the pmdapapi(1) man page for a + descript of the customiztion parameters. + +Removal +======= + + + Simply use + + # cd $PCP_PMDAS_DIR/papi + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/papi.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/papi/Remove b/src/pmdas/papi/Remove new file mode 100755 index 0000000..8d04be2 --- /dev/null +++ b/src/pmdas/papi/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the papi PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=papi + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/papi/help b/src/pmdas/papi/help new file mode 100644 index 0000000..2981ee5 --- /dev/null +++ b/src/pmdas/papi/help @@ -0,0 +1,74 @@ +# +# Copyright (c) 2014 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# papi PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# +@ papi.TOT_INS total instruction completed + +@ papi.TOT_CYC total cycles completed + +@ papi.L1_DCM Level 1 data cache misses + +@ papi.L1_ICM Level 1 instruction cache misses + +@ papi.L2_DCM Level 2 data cache misses + +@ papi.L2_ICM Level 2 instruction cache misses + +@ papi.L3_DCM Level 3 data cache misses + +@ papi.L3_ICM Level 3 instruction cache misses + +@ papi.L1_TCM Level 1 total cache misses + +@ papi.L2_TCM Level 2 total cache misses + +@ papi.L3_TCM Level 3 total cache misses + +@ papi.TLB_DM Data translation lookaside buffer misses + +@ papi.TLB_IM Instruction translation lookaside buffer misses + +@ papi.TLB_TL Total translation lookaside buffer misses + +@ papi.L1_LDM Level 1 load misses + +@ papi.L1_STM Level 1 store misses + +@ papi.L2_LDM Level 2 load misses + +@ papi.L2_STM Level 2 store misses + +@ papi.control.enable A list of metrics to enable and count + +@ papi.control.reset Reset the counter values + +@ papi.control.disable A list of metrics to disable counting + +@ papi.control.status A string of papi counters current state + +@ papi.available.num_counters Number of hardware counters available for use + diff --git a/src/pmdas/papi/papi.c b/src/pmdas/papi/papi.c new file mode 100644 index 0000000..4977bb1 --- /dev/null +++ b/src/pmdas/papi/papi.c @@ -0,0 +1,1497 @@ +/* + * PAPI PMDA + * + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "domain.h" +#include +#include +#if defined(HAVE_GRP_H) +#include +#endif +#include + + +enum { + CLUSTER_PAPI = 0, // hardware event counters + CLUSTER_CONTROL, // control variables + CLUSTER_AVAILABLE, // available hardware +}; + +typedef struct { + unsigned int papi_event_code; //the PAPI_ eventcode + char papi_string_code[8]; + int position; + int pmns_position; +} papi_m_user_tuple; + +static papi_m_user_tuple *papi_info; + +static char *enable_string; +static char *disable_string; +static char isDSO = 1; /* == 0 if I am a daemon */ +static int EventSet = PAPI_NULL; +static long_long *values; +struct uid_gid_tuple { + char uid_p; char gid_p; /* uid/gid received flags. */ + int uid; int gid; /* uid/gid received from PCP_ATTR_* */ +}; +static struct uid_gid_tuple *ctxtab; +static int ctxtab_size; +static int number_of_counters; +static unsigned int number_of_active_counters; +static unsigned int size_of_active_counters; +static unsigned int number_of_events; + +static void +set_pmns_position(unsigned int i) +{ + switch (papi_info[i].papi_event_code) { + case PAPI_TOT_INS: + papi_info[i].pmns_position = 0; + break; + case PAPI_TOT_CYC: + papi_info[i].pmns_position = 1; + break; + case PAPI_L1_DCM: + papi_info[i].pmns_position = 2; + break; + case PAPI_L1_ICM: + papi_info[i].pmns_position = 3; + break; + case PAPI_L2_DCM: + papi_info[i].pmns_position = 4; + break; + case PAPI_L2_ICM: + papi_info[i].pmns_position = 5; + break; + case PAPI_L3_DCM: + papi_info[i].pmns_position = 6; + break; + case PAPI_L3_ICM: + papi_info[i].pmns_position = 7; + break; + case PAPI_L1_TCM: + papi_info[i].pmns_position = 8; + break; + case PAPI_L2_TCM: + papi_info[i].pmns_position = 9; + break; + case PAPI_L3_TCM: + papi_info[i].pmns_position = 10; + break; + case PAPI_TLB_DM: + papi_info[i].pmns_position = 11; + break; + case PAPI_TLB_IM: + papi_info[i].pmns_position = 12; + break; + case PAPI_TLB_TL: + papi_info[i].pmns_position = 13; + break; + case PAPI_L1_LDM: + papi_info[i].pmns_position = 14; + break; + case PAPI_L1_STM: + papi_info[i].pmns_position = 15; + break; + case PAPI_L2_LDM: + papi_info[i].pmns_position = 16; + break; + case PAPI_L2_STM: + papi_info[i].pmns_position = 17; + break; + case PAPI_CA_SNP: + papi_info[i].pmns_position = 18; + break; + case PAPI_CA_SHR: + papi_info[i].pmns_position = 19; + break; + case PAPI_CA_CLN: + papi_info[i].pmns_position = 20; + break; + case PAPI_CA_INV: + papi_info[i].pmns_position = 21; + break; + case PAPI_CA_ITV: + papi_info[i].pmns_position = 22; + break; + case PAPI_L3_LDM: + papi_info[i].pmns_position = 23; + break; + case PAPI_L3_STM: + papi_info[i].pmns_position = 24; + break; + case PAPI_BRU_IDL: + papi_info[i].pmns_position = 25; + break; + case PAPI_FXU_IDL: + papi_info[i].pmns_position = 26; + break; + case PAPI_FPU_IDL: + papi_info[i].pmns_position = 27; + break; + case PAPI_LSU_IDL: + papi_info[i].pmns_position = 28; + break; + case PAPI_BTAC_M: + papi_info[i].pmns_position = 29; + break; + case PAPI_PRF_DM: + papi_info[i].pmns_position = 30; + break; + case PAPI_L3_DCH: + papi_info[i].pmns_position = 31; + break; + case PAPI_TLB_SD: + papi_info[i].pmns_position = 32; + break; + case PAPI_CSR_FAL: + papi_info[i].pmns_position = 33; + break; + case PAPI_CSR_SUC: + papi_info[i].pmns_position = 34; + break; + case PAPI_CSR_TOT: + papi_info[i].pmns_position = 35; + break; + case PAPI_MEM_SCY: + papi_info[i].pmns_position = 36; + break; + case PAPI_MEM_RCY: + papi_info[i].pmns_position = 37; + break; + case PAPI_MEM_WCY: + papi_info[i].pmns_position = 38; + break; + case PAPI_STL_ICY: + papi_info[i].pmns_position = 39; + break; + case PAPI_FUL_ICY: + papi_info[i].pmns_position = 40; + break; + case PAPI_STL_CCY: + papi_info[i].pmns_position = 41; + break; + case PAPI_FUL_CCY: + papi_info[i].pmns_position = 42; + break; + case PAPI_HW_INT: + papi_info[i].pmns_position = 43; + break; + case PAPI_BR_UCN: + papi_info[i].pmns_position = 44; + break; + case PAPI_BR_CN: + papi_info[i].pmns_position = 45; + break; + case PAPI_BR_TKN: + papi_info[i].pmns_position = 46; + break; + case PAPI_BR_NTK: + papi_info[i].pmns_position = 47; + break; + case PAPI_BR_MSP: + papi_info[i].pmns_position = 48; + break; + case PAPI_BR_PRC: + papi_info[i].pmns_position = 49; + break; + case PAPI_FMA_INS: + papi_info[i].pmns_position = 50; + break; + case PAPI_TOT_IIS: + papi_info[i].pmns_position = 51; + break; + case PAPI_INT_INS: + papi_info[i].pmns_position = 52; + break; + case PAPI_FP_INS: + papi_info[i].pmns_position = 53; + break; + case PAPI_LD_INS: + papi_info[i].pmns_position = 54; + break; + case PAPI_SR_INS: + papi_info[i].pmns_position = 55; + break; + case PAPI_BR_INS: + papi_info[i].pmns_position = 56; + break; + case PAPI_VEC_INS: + papi_info[i].pmns_position = 57; + break; + case PAPI_RES_STL: + papi_info[i].pmns_position = 58; + break; + case PAPI_FP_STAL: + papi_info[i].pmns_position = 59; + break; + case PAPI_LST_INS: + papi_info[i].pmns_position = 60; + break; + case PAPI_SYC_INS: + papi_info[i].pmns_position = 61; + break; + case PAPI_L1_DCH: + papi_info[i].pmns_position = 62; + break; + case PAPI_L2_DCH: + papi_info[i].pmns_position = 63; + break; + case PAPI_L1_DCA: + papi_info[i].pmns_position = 64; + break; + case PAPI_L2_DCA: + papi_info[i].pmns_position = 65; + break; + case PAPI_L3_DCA: + papi_info[i].pmns_position = 66; + break; + case PAPI_L1_DCR: + papi_info[i].pmns_position = 67; + break; + case PAPI_L2_DCR: + papi_info[i].pmns_position = 68; + break; + case PAPI_L3_DCR: + papi_info[i].pmns_position = 69; + break; + case PAPI_L1_DCW: + papi_info[i].pmns_position = 70; + break; + case PAPI_L2_DCW: + papi_info[i].pmns_position = 71; + break; + case PAPI_L3_DCW: + papi_info[i].pmns_position = 72; + break; + case PAPI_L1_ICH: + papi_info[i].pmns_position = 73; + break; + case PAPI_L2_ICH: + papi_info[i].pmns_position = 74; + break; + case PAPI_L3_ICH: + papi_info[i].pmns_position = 75; + break; + case PAPI_L1_ICA: + papi_info[i].pmns_position = 76; + break; + case PAPI_L2_ICA: + papi_info[i].pmns_position = 77; + break; + case PAPI_L3_ICA: + papi_info[i].pmns_position = 78; + break; + case PAPI_L1_ICR: + papi_info[i].pmns_position = 79; + break; + case PAPI_L2_ICR: + papi_info[i].pmns_position = 80; + break; + case PAPI_L3_ICR: + papi_info[i].pmns_position = 81; + break; + case PAPI_L1_ICW: + papi_info[i].pmns_position = 82; + break; + case PAPI_L2_ICW: + papi_info[i].pmns_position = 83; + break; + case PAPI_L3_ICW: + papi_info[i].pmns_position = 84; + break; + case PAPI_L1_TCH: + papi_info[i].pmns_position = 85; + break; + case PAPI_L2_TCH: + papi_info[i].pmns_position = 86; + break; + case PAPI_L3_TCH: + papi_info[i].pmns_position = 87; + break; + case PAPI_L1_TCA: + papi_info[i].pmns_position = 88; + break; + case PAPI_L2_TCA: + papi_info[i].pmns_position = 89; + break; + case PAPI_L3_TCA: + papi_info[i].pmns_position = 90; + break; + case PAPI_L1_TCR: + papi_info[i].pmns_position = 91; + break; + case PAPI_L2_TCR: + papi_info[i].pmns_position = 92; + break; + case PAPI_L3_TCR: + papi_info[i].pmns_position = 93; + break; + case PAPI_L1_TCW: + papi_info[i].pmns_position = 94; + break; + case PAPI_L2_TCW: + papi_info[i].pmns_position = 95; + break; + case PAPI_L3_TCW: + papi_info[i].pmns_position = 96; + break; + case PAPI_FML_INS: + papi_info[i].pmns_position = 97; + break; + case PAPI_FAD_INS: + papi_info[i].pmns_position = 98; + break; + case PAPI_FDV_INS: + papi_info[i].pmns_position = 99; + break; + case PAPI_FSQ_INS: + papi_info[i].pmns_position = 100; + break; + case PAPI_FNV_INS: + papi_info[i].pmns_position = 101; + break; + case PAPI_FP_OPS: + papi_info[i].pmns_position = 102; + break; + case PAPI_SP_OPS: + papi_info[i].pmns_position = 103; + break; + case PAPI_DP_OPS: + papi_info[i].pmns_position = 104; + break; + case PAPI_VEC_SP: + papi_info[i].pmns_position = 105; + break; + case PAPI_VEC_DP: + papi_info[i].pmns_position = 106; + break; +#ifdef PAPI_REF_CYC + case PAPI_REF_CYC: + papi_info[i].pmns_position = 107; + break; +#endif + default: + papi_info[i].pmns_position = -1; + break; + } +} + +static int +permission_check(int context) +{ + if ((ctxtab[context].uid_p && ctxtab[context].uid == 0) || + (ctxtab[context].gid_p && ctxtab[context].gid == 0)) + return 1; + return 0; +} + +static void +expand_papi_info(int size) +{ + if (number_of_events <= size) { + size_t new_size = (size + 1) * sizeof(papi_m_user_tuple); + papi_info = realloc(papi_info, new_size); + if (papi_info == NULL) + __pmNoMem("papi_info tuple", new_size, PM_FATAL_ERR); + while (number_of_events <= size) + memset(&papi_info[number_of_events++], 0, sizeof(papi_m_user_tuple)); + } +} + +static void +expand_values(int size) +{ + if (size_of_active_counters <= size) { + size_t new_size = (size + 1) * sizeof(long_long); + values = realloc(values, new_size); + if (values == NULL) + __pmNoMem("values", new_size, PM_FATAL_ERR); + while (size_of_active_counters <= size) { + memset(&values[size_of_active_counters++], 0, sizeof(long_long)); + if (pmDebug & DBG_TRACE_APPL0) { + __pmNotifyErr(LOG_DEBUG, "memsetting to zero, %d counters\n", + size_of_active_counters); + } + } + } +} + +static void +enlarge_ctxtab(int context) +{ + /* Grow the context table if necessary. */ + if (ctxtab_size /* cardinal */ <= context /* ordinal */) { + size_t need = (context + 1) * sizeof(struct uid_gid_tuple); + ctxtab = realloc(ctxtab, need); + if (ctxtab == NULL) + __pmNoMem("papi ctx table", need, PM_FATAL_ERR); + /* Blank out new entries. */ + while (ctxtab_size <= context) + memset(&ctxtab[ctxtab_size++], 0, sizeof(struct uid_gid_tuple)); + } +} + +static int +check_papi_state(int state) +{ + int retval; + retval = PAPI_state(EventSet, &state); + if (retval != PAPI_OK) + return PM_ERR_NODATA; + return state; +} + +static char * +papi_string_status(void) +{ + int state, retval; + + retval = PAPI_state(EventSet, &state); + if (retval != PAPI_OK) + return "PAPI_state error."; + switch (state) { + case PAPI_STOPPED: + return "Papi is stopped."; + case PAPI_RUNNING: + return "Papi is running."; + case PAPI_PAUSED: + return "Papi is paused"; + case PAPI_NOT_INIT: + return "Papi eventset is defined but not initialized."; + case PAPI_OVERFLOWING: + return "Papi eventset has overflowing enabled"; + case PAPI_PROFILING: + return "Papi eventset has profiling enabled"; + case PAPI_MULTIPLEXING: + return "Papi eventset has multiplexing enabled"; + case PAPI_ATTACHED: + return "Papi is attached to another process/thread"; + case PAPI_CPU_ATTACHED: + return "Papi is attached to a specific CPU."; + default: + return "PAPI_state error."; + } +} + +/* + * A list of all the papi metrics we support - + */ +static pmdaMetric metrictab[] = { + { NULL, + { PMDA_PMID(CLUSTER_PAPI,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TOT_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TOT_CYC */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_DCM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_ICM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_DCM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_ICM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_DCM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_ICM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_TCM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_TCM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_TCM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TLB_DM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TLB_IM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TLB_TL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_LDM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_STM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_LDM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_STM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CA_SNP */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,19), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CA_SHR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CA_CLN */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,21), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CA_INV */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CA_ITV */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_LDM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,24), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_STM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,25), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BRU_IDL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FXU_IDL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FPU_IDL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.LSU_IDL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BTAC_M */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.PRF_DM */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_DCH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TLB_SD */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CSR_FAL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CSR_SUC */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.CSR_TOT */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.MEM_SCY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.MEM_RCY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,38), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.MEM_WCY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,39), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.STL_ICY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,40), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FUL_ICY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,41), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.STL_CCY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,42), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FUL_CCY */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,43), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.HW_INT */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,44), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_UCN */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_CN */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_TKN */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,47), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_NTK */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_MSP */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,49), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_PRC */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,50), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FMA_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.TOT_IIS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.INT_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FP_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.LD_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.SR_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.BR_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.VEC_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.RES_STL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FP_STAL */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.LST_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,61), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.SYC_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,62), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_DCH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,63), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_DCH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,64), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_DCA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,65), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_DCA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,66), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_DCA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,67), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_DCR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,68), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_DCR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,69), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_DCR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,70), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_DCW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_DCW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_DCW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,73), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_ICH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_ICH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_ICH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_ICA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,77), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_ICA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,78), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_ICA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,79), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_ICR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,80), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_ICR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,81), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_ICR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,82), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_ICW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,83), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_ICW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,84), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_ICW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,85), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_TCH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,86), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_TCH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,87), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_TCH */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,88), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_TCA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,89), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_TCA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,90), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_TCA */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,91), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_TCR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,92), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_TCR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,93), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_TCR */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,94), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L1_TCW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,95), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L2_TCW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,96), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.L3_TCW */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,97), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FML_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,98), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FAD_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,99), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FDV_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,100), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FSQ_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,101), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FNV_INS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,102), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.FP_OPS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,103), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.SP_OPS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,104), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.DP_OPS */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,105), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.VEC_SP */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,106), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.VEC_DP */ + + { NULL, + { PMDA_PMID(CLUSTER_PAPI,107), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.REF_CYC */ + + { NULL, + { PMDA_PMID(CLUSTER_CONTROL,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.control.enable */ + + { NULL, + { PMDA_PMID(CLUSTER_CONTROL,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.control.reset */ + + { NULL, + { PMDA_PMID(CLUSTER_CONTROL,2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.control.disable */ + + { NULL, + { PMDA_PMID(CLUSTER_CONTROL,3), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.control.status */ + + { NULL, + { PMDA_PMID(CLUSTER_AVAILABLE,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, /* papi.available.num_counters */ + +}; + +static void +papi_endContextCallBack(int context) +{ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "end context %d received\n", context); + + /* ensure clients re-using this slot re-authenticate */ + if (context >= 0 && context < ctxtab_size) { + ctxtab[context].uid_p = 0; + ctxtab[context].gid_p = 0; + } +} + +static int +papi_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + int running = 0; + int sts = 0; + int i; + + sts = check_papi_state(sts); + if (sts == PAPI_RUNNING && idp->cluster == CLUSTER_PAPI) { + sts = PAPI_read(EventSet, values); + if (sts != PAPI_OK) { + __pmNotifyErr(LOG_ERR, "PAPI_read: %s\n", PAPI_strerror(sts)); + return PM_ERR_VALUE; + } + running = 1; + } + + switch (idp->cluster) { + case CLUSTER_PAPI: + if (!running) + return PMDA_FETCH_NOVALUES; + if (idp->item >= 0 && idp->item <= 107) { + // the 'case' && 'idp->item' value we get is the pmns_position + for (i = 0; i < number_of_events; i++) { + if (papi_info[i].pmns_position == idp->item) { + if(papi_info[i].position >= 0 && papi_info[i].papi_event_code){ + atom->ull = values[papi_info[i].position]; + return PMDA_FETCH_STATIC; + } + else + return PMDA_FETCH_NOVALUES; + } + } + } + return PM_ERR_PMID; + + case CLUSTER_CONTROL: + switch (idp->item) { + case 0: + atom->cp = enable_string; /* papi.control.enable */ + return PMDA_FETCH_STATIC; + + case 1: + // break; /* papi.control.reset */ + // atom->cp = reset_string; + return PM_ERR_NYI; + + case 2: + if ((sts = check_papi_state(sts)) == PAPI_RUNNING) { + atom->cp = disable_string; /* papi.control.disable */ + return PMDA_FETCH_STATIC; + } + return 0; + + case 3: + atom->cp = papi_string_status(); /* papi.control.status */ + return PMDA_FETCH_STATIC; + + default: + return PM_ERR_PMID; + } + break; + + case CLUSTER_AVAILABLE: + if (idp->item == 0) { + atom->ul = number_of_counters; /* papi.available.num_counters */ + return PMDA_FETCH_STATIC; + } + return PM_ERR_PMID; + + default: + return PM_ERR_PMID; + } + + return PMDA_FETCH_NOVALUES; +} + +static int +papi_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + if (permission_check(pmda->e_context)) + return pmdaFetch(numpmid, pmidlist, resp, pmda); + return PM_ERR_PERMISSION; +} + +static int +remove_metric(unsigned int event, int position) +{ + int retval = 0; + int state = 0; + int restart = 0; // bool to restart running values at the end + int i; + long_long new_values[size_of_active_counters]; + + retval = PAPI_query_event(event); + if (retval != PAPI_OK){ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "event not found on this hardware, skipping\n"); + return retval; + } + + /* check to make sure papi is running, otherwise do nothing */ + state = check_papi_state(state); + if (state == PAPI_RUNNING) { + restart = 1; + retval = PAPI_stop(EventSet, values); + if(retval != PAPI_OK) + return retval; + } + state = check_papi_state(state); + if (state == PAPI_STOPPED) { + /* first, copy the values over to new array */ + for (i = 0; i < number_of_events; i++) + new_values[papi_info[i].position] = values[papi_info[i].position]; + + /* workaround a papi bug: fully destroy the eventset and restart it */ + memset(values, 0, sizeof(values[0])*size_of_active_counters); + retval = PAPI_cleanup_eventset(EventSet); + if (retval != PAPI_OK) + return retval; + + retval = PAPI_destroy_eventset(&EventSet); + if (retval != PAPI_OK) + return retval; + + number_of_active_counters--; + retval = PAPI_create_eventset(&EventSet); + if (retval != PAPI_OK) + return retval; + + // run through all metrics and adjust position variable as needed + for (i = 0; i < number_of_events; i++) { + // set event we're removing position to -1 + if (papi_info[i].position == position) { + new_values[papi_info[i].position] = 0; + papi_info[i].position = -1; + } + } + + for (i = 0; i < number_of_events; i++) { + if (papi_info[i].position < position) + values[papi_info[i].position] = new_values[papi_info[i].position]; + + if (papi_info[i].position > position) { + papi_info[i].position--; + values[papi_info[i].position] = new_values[papi_info[i].position+1]; + } + if (papi_info[i].position >= 0 && papi_info[i].papi_event_code) { + retval = PAPI_add_event(EventSet, papi_info[i].papi_event_code); + if (retval != PAPI_OK) + return retval; + } + } + if (restart && (number_of_active_counters > 0)) { + retval = PAPI_start(EventSet); + if (retval != PAPI_OK) + return retval; + } + return retval; + } + return PM_ERR_VALUE; +} + +static int +add_metric(unsigned int event) +{ + int retval = 0; + int state = 0; + long_long new_values[size_of_active_counters]; + int i; + + retval = PAPI_query_event(event); + if (retval != PAPI_OK){ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "event not found on this hardware, skipping\n"); + return retval; + } + /* check status of papi */ + state = check_papi_state(state); + /* add check with number_of_counters */ + /* stop papi if running? */ + if (state == PAPI_RUNNING) { + for (i = 0; i < size_of_active_counters; i++){ + if(papi_info[i].position >= 0 && papi_info[i].papi_event_code) + new_values[papi_info[i].position] = values[papi_info[i].position]; + } + retval = PAPI_stop(EventSet, values); + if (retval != PAPI_OK) + return retval; + } + state = check_papi_state(state); + if (state == PAPI_STOPPED) { + /* add metric */ + retval = PAPI_add_event(EventSet, event); //XXX possibly switch this to add_events + if (retval != PAPI_OK) + return retval; + + for (i = 0; i < size_of_active_counters; i++){ + if(papi_info[i].position >= 0 && papi_info[i].papi_event_code) + values[papi_info[i].position] = new_values[papi_info[i].position]; + } + number_of_active_counters++; + retval = PAPI_start(EventSet); + return retval; + } + return PM_ERR_VALUE; +} + +static int +papi_store(pmResult *result, pmdaExt *pmda) +{ + int sts; + int i, j, len; + const char *delim = " ,"; + char *substring; + + if (!permission_check(pmda->e_context)) + return PM_ERR_PERMISSION; + for (i = 0; i < result->numpmid; i++) { + pmValueSet *vsp = result->vset[i]; + __pmID_int *idp = (__pmID_int *)&(vsp->pmid); + pmAtomValue av; + + if (idp->cluster != CLUSTER_CONTROL) + return PM_ERR_PERMISSION; + + switch (idp->item) { + case 0: //papi.enable + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_STRING, &av, PM_TYPE_STRING)) < 0) + return sts; + free(enable_string); + enable_string = av.cp; + len = strlen(enable_string); + substring = strtok(enable_string, delim); + while (substring != NULL) { + for (j = 0; j < number_of_events; j++) { + if (!strcmp(substring, papi_info[j].papi_string_code) && papi_info[j].position < 0) { + // add the metric to the set if it's not already there + sts = add_metric(papi_info[j].papi_event_code); + if (sts == PAPI_OK) + papi_info[j].position = number_of_active_counters-1; + } + } + substring = strtok(NULL, delim); + } + for (j = 0; j < len-1; j++) { // recover from tokenisation + if (enable_string[j] == '\0') + enable_string[j] = delim[0]; + } + break; + + case 1: //papi.reset +#if 0 /* not yet implemented */ + sts = check_papi_state(sts); + if (sts == PAPI_RUNNING) { + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_STRING, &av, PM_TYPE_STRING)) < 0) + return sts; + } + sts = PAPI_reset(EventSet); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "reset: %d\n", sts); + if (sts != PAPI_OK) + return PM_ERR_VALUE; + break; +#else + return PM_ERR_NYI; +#endif + + case 2: //papi.disable + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_STRING, &av, PM_TYPE_STRING)) < 0) + return sts; + free(disable_string); + disable_string = av.cp; + len = strlen(disable_string); + substring = strtok(disable_string, delim); + while (substring != NULL) { + for (j = 0; j < size_of_active_counters; j++) { + if (!strcmp(substring, papi_info[j].papi_string_code)) { + // remove the metric from the set + sts = remove_metric(papi_info[j].papi_event_code, papi_info[j].position); + if (sts == PAPI_OK) + papi_info[j].position = -1; + break; //we've found the correct metric, break; + } + } + if (j == size_of_active_counters) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "metric name %s does not match any known metrics\n", substring); + sts = 1; + } + substring = strtok(NULL, delim); + } + for (j = 0; j < len-1; j++) { // recover from tokenisation + if (disable_string[j] == '\0') + disable_string[j] = delim[0]; + } + if (sts) + return PM_ERR_CONV; + break; + + default: + return PM_ERR_PMID; + } + } + return 0; +} + +static int +papi_text(int ident, int type, char **buffer, pmdaExt *ep) +{ + int ec; + int i; + int position = -1; + PAPI_event_info_t info; + __pmID_int *pmidp = (__pmID_int*)&ident; + + /* no indoms - we only deal with metric help text */ + if ((type & PM_TEXT_PMID) != PM_TEXT_PMID) + return PM_ERR_TEXT; + + ec = 0 | PAPI_PRESET_MASK; + PAPI_enum_event(&ec, PAPI_ENUM_FIRST); + for (i = 0; i < number_of_events; i++) { + if (pmidp->item == papi_info[i].pmns_position) { + position = i; + break; + } + } + + do { + if (PAPI_get_event_info(ec, &info) == PAPI_OK) { + if (info.event_code == papi_info[position].papi_event_code) { + if (type & PM_TEXT_ONELINE) + *buffer = info.short_descr; + else + *buffer = info.long_descr; + return 0; + } + } + } while (PAPI_enum_event(&ec, 0) == PAPI_OK); + + return pmdaText(ident, type, buffer, ep); +} + +static int +papi_internal_init(void) +{ + int ec; + int sts; + int addunderscore; + PAPI_event_info_t info; + char *substr; + char concatstr[10] = {}; + unsigned int i = 0; + + number_of_counters = PAPI_num_counters(); + if (number_of_counters < 0){ + __pmNotifyErr(LOG_ERR, "hardware does not support hardware counters\n"); + return 1; + } + + ec = 0 | PAPI_PRESET_MASK; + sts = PAPI_library_init(PAPI_VER_CURRENT); + if (sts != PAPI_VER_CURRENT) { + __pmNotifyErr(LOG_DEBUG, "PAPI_library_init error!\n"); + return PM_ERR_GENERIC; + } + + sts = PAPI_set_domain(PAPI_DOM_ALL); + if (sts != PAPI_OK) { + __pmNotifyErr(LOG_DEBUG, "Cannot set the domain to PAPI_DOM_ALL.\n"); + return PM_ERR_GENERIC; + } + + enable_string = (char *)calloc(1, 1); + disable_string = (char *)calloc(1, 1); + PAPI_enum_event(&ec, PAPI_ENUM_FIRST); + do { + if (PAPI_get_event_info(ec, &info) == PAPI_OK) { + i++; + expand_papi_info(i); + papi_info[i-1].papi_event_code = info.event_code; + substr = strtok(info.symbol, "_"); + while (substr != NULL) { + addunderscore = 0; + if (strcmp("PAPI",substr)) { + addunderscore = 1; + strcat(concatstr, substr); + } + substr = strtok(NULL, "_"); + if (substr != NULL && addunderscore) { + strcat(concatstr, "_"); + } + } + strcpy(papi_info[i-1].papi_string_code, concatstr); + memset(&concatstr[0], 0, sizeof(concatstr)); + papi_info[i-1].position = -1; + set_pmns_position(i-1); + } + } while(PAPI_enum_event(&ec, 0) == PAPI_OK); + expand_values(i); + if (PAPI_create_eventset(&EventSet) != PAPI_OK) { + __pmNotifyErr(LOG_ERR, "PAPI_create_eventset error!\n"); + return PM_ERR_GENERIC; + } + return 0; + +} + +/* use documented in pmdaAttribute(3) */ +static int +papi_contextAttributeCallBack(int context, int attr, + const char *value, int length, pmdaExt *pmda) +{ + int id = -1; + + enlarge_ctxtab(context); + assert(ctxtab != NULL && context < ctxtab_size); + + switch (attr) { + case PCP_ATTR_USERID: + ctxtab[context].uid_p = 1; + id = atoi(value); + ctxtab[context].uid = id; + break; + + case PCP_ATTR_GROUPID: + ctxtab[context].gid_p = 1; + id = atoi(value); + ctxtab[context].gid = id; + break; + + default: + return 0; + } + + if (id != 0) { + if (pmDebug & DBG_TRACE_AUTH) + __pmNotifyErr(LOG_DEBUG, "access denied attr=%d id=%d\n", attr, id); + return PM_ERR_PERMISSION; + } + else if (pmDebug & DBG_TRACE_AUTH) + __pmNotifyErr(LOG_DEBUG, "access granted attr=%d id=%d\n", attr, id); + + return 0; +} + +void +__PMDA_INIT_CALL +papi_init(pmdaInterface *dp) +{ + int nummetrics = sizeof(metrictab)/sizeof(metrictab[0]); + int sts; + + if (isDSO) { + char mypath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + + snprintf(mypath, sizeof(mypath), "%s%c" "papi" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_6, "papi DSO", mypath); + } + + if (dp->status != 0) + return; + + dp->comm.flags |= PDU_FLAG_AUTH; + + if ((sts = papi_internal_init()) != 0) { + __pmNotifyErr(LOG_ERR, "papi_internal_init returned %d\n", sts); + dp->status = PM_ERR_GENERIC; + return; + } + + dp->version.six.fetch = papi_fetch; + dp->version.six.store = papi_store; + dp->version.six.attribute = papi_contextAttributeCallBack; + dp->version.any.text = papi_text; + pmdaSetFetchCallBack(dp, papi_fetchCallBack); + pmdaSetEndContextCallBack(dp, papi_endContextCallBack); + pmdaInit(dp, NULL, 0, metrictab, nummetrics); +} + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +static pmdaOptions opts = { + .short_options = "D:d:l:?", + .long_options = longopts, +}; + +/* + * Set up agent if running as daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + + isDSO = 0; + __pmSetProgname(argv[0]); + + snprintf(helppath, sizeof(helppath), "%s%c" "papi" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_6, pmProgname, PAPI, "papi.log", helppath); + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + pmdaOpenLog(&dispatch); + papi_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + + free(ctxtab); + free(papi_info); + free(values); + + exit(0); +} diff --git a/src/pmdas/papi/pmns b/src/pmdas/papi/pmns new file mode 100644 index 0000000..d4b8eac --- /dev/null +++ b/src/pmdas/papi/pmns @@ -0,0 +1,139 @@ +/* + * Metrics for papi PMDA + * + * Copyright (c) 2014 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +papi { + TOT_INS PAPI:0:0 + TOT_CYC PAPI:0:1 + L1_DCM PAPI:0:2 + L1_ICM PAPI:0:3 + L2_DCM PAPI:0:4 + L2_ICM PAPI:0:5 + L3_DCM PAPI:0:6 + L3_ICM PAPI:0:7 + L1_TCM PAPI:0:8 + L2_TCM PAPI:0:9 + L3_TCM PAPI:0:10 + TLB_DM PAPI:0:11 + TLB_IM PAPI:0:12 + TLB_TL PAPI:0:13 + L1_LDM PAPI:0:14 + L1_STM PAPI:0:15 + L2_LDM PAPI:0:16 + L2_STM PAPI:0:17 + CA_SNP PAPI:0:18 + CA_SHR PAPI:0:19 + CA_CLN PAPI:0:20 + CA_INV PAPI:0:21 + CA_ITV PAPI:0:22 + L3_LDM PAPI:0:23 + L3_STM PAPI:0:24 + BRU_IDL PAPI:0:25 + FXU_IDL PAPI:0:26 + FPU_IDL PAPI:0:27 + LSU_IDL PAPI:0:28 + BTAC_M PAPI:0:29 + PRF_DM PAPI:0:30 + L3_DCH PAPI:0:31 + TLB_SD PAPI:0:32 + CSR_FAL PAPI:0:33 + CSR_SUC PAPI:0:34 + CSR_TOT PAPI:0:35 + MEM_SCY PAPI:0:36 + MEM_RCY PAPI:0:37 + MEM_WCY PAPI:0:38 + STL_ICY PAPI:0:39 + FUL_ICY PAPI:0:40 + STL_CCY PAPI:0:41 + FUL_CCY PAPI:0:42 + HW_INT PAPI:0:43 + BR_UCN PAPI:0:44 + BR_CN PAPI:0:45 + BR_TKN PAPI:0:46 + BR_NTK PAPI:0:47 + BR_MSP PAPI:0:48 + BR_PRC PAPI:0:49 + FMA_INS PAPI:0:50 + TOT_IIS PAPI:0:51 + INT_INS PAPI:0:52 + FP_INS PAPI:0:53 + LD_INS PAPI:0:54 + SR_INS PAPI:0:55 + BR_INS PAPI:0:56 + VEC_INS PAPI:0:57 + RES_STL PAPI:0:58 + FP_STAL PAPI:0:59 + LST_INS PAPI:0:60 + SYC_INS PAPI:0:61 + L1_DCH PAPI:0:62 + L2_DCH PAPI:0:63 + L1_DCA PAPI:0:64 + L2_DCA PAPI:0:65 + L3_DCA PAPI:0:66 + L1_DCR PAPI:0:67 + L2_DCR PAPI:0:68 + L3_DCR PAPI:0:69 + L1_DCW PAPI:0:70 + L2_DCW PAPI:0:71 + L3_DCW PAPI:0:72 + L1_ICH PAPI:0:73 + L2_ICH PAPI:0:74 + L3_ICH PAPI:0:75 + L1_ICA PAPI:0:76 + L2_ICA PAPI:0:77 + L3_ICA PAPI:0:78 + L1_ICR PAPI:0:79 + L2_ICR PAPI:0:80 + L3_ICR PAPI:0:81 + L1_ICW PAPI:0:82 + L2_ICW PAPI:0:83 + L3_ICW PAPI:0:84 + L1_TCH PAPI:0:85 + L2_TCH PAPI:0:86 + L3_TCH PAPI:0:87 + L1_TCA PAPI:0:88 + L2_TCA PAPI:0:89 + L3_TCA PAPI:0:90 + L1_TCR PAPI:0:91 + L2_TCR PAPI:0:92 + L3_TCR PAPI:0:93 + L1_TCW PAPI:0:94 + L2_TCW PAPI:0:95 + L3_TCW PAPI:0:96 + FML_INS PAPI:0:97 + FAD_INS PAPI:0:98 + FDV_INS PAPI:0:99 + FSQ_INS PAPI:0:100 + FNV_INS PAPI:0:101 + FP_OPS PAPI:0:102 + SP_OPS PAPI:0:103 + DP_OPS PAPI:0:104 + VEC_SP PAPI:0:105 + VEC_DP PAPI:0:106 + REF_CYC PAPI:0:107 + control + available +} + +papi.control { + enable PAPI:1:0 + reset PAPI:1:1 + disable PAPI:1:2 + status PAPI:1:3 +} + +papi.available { + num_counters PAPI:2:0 +} diff --git a/src/pmdas/papi/root b/src/pmdas/papi/root new file mode 100644 index 0000000..d4dfbed --- /dev/null +++ b/src/pmdas/papi/root @@ -0,0 +1,9 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { papi } + +#include "pmns" diff --git a/src/pmdas/pdns/GNUmakefile b/src/pmdas/pdns/GNUmakefile new file mode 100644 index 0000000..d5496d3 --- /dev/null +++ b/src/pmdas/pdns/GNUmakefile @@ -0,0 +1,48 @@ +#!gmake +# +# Copyright (c) 2009 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = pdns +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/pdns/Install b/src/pmdas/pdns/Install new file mode 100644 index 0000000..afc09ea --- /dev/null +++ b/src/pmdas/pdns/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2009 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the pdns PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=pdns +perl_opt=true +daemon_opt=false +forced_restart=true + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/pdns/Remove b/src/pmdas/pdns/Remove new file mode 100644 index 0000000..372e843 --- /dev/null +++ b/src/pmdas/pdns/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2009 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the pdns PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=pdns + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/pdns/pmdapdns.pl b/src/pmdas/pdns/pmdapdns.pl new file mode 100644 index 0000000..a6177d0 --- /dev/null +++ b/src/pmdas/pdns/pmdapdns.pl @@ -0,0 +1,395 @@ +# +# Copyright (c) 2009-2011 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use Time::HiRes qw ( time ); + +use vars qw( $pmda ); +my $pdns_control = 'pdns_control'; +my $rec_control = 'rec_control'; + +my $pdns_disable = 0; +my $rec_disable = 0; + +my $cached = 0; +my %vals = (); +my %vals_rec_answers = (); + +sub pdns_fetch +{ + my $now = time; + + if ($now - $cached > 1.0) { + # $pmda->log("pdns_fetch_callback update now:$now cached:$cached\n"); + + %vals = (); + %vals_rec_answers = (); + + # get the authorative server stats + if ($pdns_disable == 0 && open(PIPE, "$pdns_control list |")) { + $_ = ; + close PIPE; + + $_ =~ s/-/_/g; + $_ =~ s/,$//; + for my $kv (split(/,/, $_)) { + if ("$kv" eq '') { + last; + } + + my ($k, $v) = split(/=/, $kv); + $vals{$k} = $v; + } + } else { + $pdns_disable = 1; + } + + # get the recursive server stats + if ($rec_disable == 0 && open(PIPE, "$rec_control get-all |")) { + while($_ = ) { + $_ =~ s/-/_/g; + my ($k, $v) = split(/\t/, $_); + + if ($k eq 'answers0_1') { + $vals_rec_answers{0} = $v; + } elsif ($k eq 'answers1_10') { + $vals_rec_answers{1} = $v; + } elsif ($k eq 'answers10_100') { + $vals_rec_answers{2} = $v; + } elsif ($k eq 'answers100_1000') { + $vals_rec_answers{3} = $v; + } elsif ($k eq 'answers_slow') { + $vals_rec_answers{4} = $v; + } else { + $vals{"recursor.$k"} = $v; + } + } + close PIPE; + } else { + $rec_disable = 1; + } + + $cached = $now; + } +} + +sub pdns_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + + # $pmda->log("pdns_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + if (!defined($metric_name)) { return (PM_ERR_PMID, 0); } + + $metric_name =~ s/^pdns\.//; + + if ($metric_name eq 'recursor.answers') { + if ($inst == PM_IN_NULL) { return (PM_ERR_INST, 0); } + return ($vals_rec_answers{$inst}, 1) if (defined($vals_rec_answers{$inst})); + } else { + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + return ($vals{$metric_name}, 1) if (defined($vals{$metric_name})); + } + return (PM_ERR_APPVERSION, 0); +} + +$pmda = PCP::PMDA->new('pdns', 101); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.corrupt_packets", '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.deferred_cache_inserts", '', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.deferred_cache_lookup", '', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_USEC,0), + "pdns.latency", '', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.packetcache_hit", '', ''); +$pmda->add_metric(pmda_pmid(0,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.packetcache_miss", '', ''); +$pmda->add_metric(pmda_pmid(0,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.packetcache_size", '', ''); +$pmda->add_metric(pmda_pmid(0,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.qsize_q", '', ''); +$pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.query_cache_hit", '', ''); +$pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.query_cache_miss", '', ''); +$pmda->add_metric(pmda_pmid(0,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursing_answers", '', ''); +$pmda->add_metric(pmda_pmid(0,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursing_questions", '', ''); +$pmda->add_metric(pmda_pmid(0,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.servfail_packets", '', ''); +$pmda->add_metric(pmda_pmid(0,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.tcp_answers", '', ''); +$pmda->add_metric(pmda_pmid(0,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.tcp_queries", '', ''); +$pmda->add_metric(pmda_pmid(0,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.timedout_packets", '', ''); +$pmda->add_metric(pmda_pmid(0,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.udp_answers", '', ''); +$pmda->add_metric(pmda_pmid(0,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.udp_queries", '', ''); +$pmda->add_metric(pmda_pmid(0,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.udp4_answers", '', ''); +$pmda->add_metric(pmda_pmid(0,19), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.udp4_queries", '', ''); +$pmda->add_metric(pmda_pmid(0,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.udp6_answers", '', ''); +$pmda->add_metric(pmda_pmid(0,21), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.udp6_queries", '', ''); + +my $recursor_answers_indom = 1; +my @recursor_answers_dom = ( + 0 => '0-1 ms', + 1 => '1-10 ms', + 2 => '10-100 ms', + 3 => '100-1000 ms', + 4 => '1000+ ms', + ); + +$pmda->add_metric(pmda_pmid(1,0),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.all_outqueries", + 'counts the number of outgoing UDP queries since starting', ''); +$pmda->add_metric(pmda_pmid(1,1),PM_TYPE_U64, $recursor_answers_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.answers", + 'counts the number of queries answered within X miliseconds', ''); +$pmda->add_metric(pmda_pmid(1,2),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.cache_entries", + 'the number of entries in the cache', ''); +$pmda->add_metric(pmda_pmid(1,3),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.cache_hits", + 'counts the number of cache hits since starting', ''); +$pmda->add_metric(pmda_pmid(1,4),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.cache_misses", + 'counts the number of cache misses since starting', ''); +$pmda->add_metric(pmda_pmid(1,5),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.chain_resends", + 'number of queries chained to existing outstanding query', ''); +$pmda->add_metric(pmda_pmid(1,6),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.client_parse_errors", + 'counts number of client packets that could not be parsed', ''); +$pmda->add_metric(pmda_pmid(1,7),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.concurrent_queries", + 'shows the number of MThreads currently running', ''); +$pmda->add_metric(pmda_pmid(1,8),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.dlg_only_drops", + 'number of records dropped because of delegation only setting', ''); +$pmda->add_metric(pmda_pmid(1,9),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.dont_outqueries", + 'number of outgoing queries dropped because of "dont-query" setting', ''); +$pmda->add_metric(pmda_pmid(1,10),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.ipv6_outqueries", + 'number of outgoing queries over IPv6', ''); +$pmda->add_metric(pmda_pmid(1,11),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.negcache_entries", + 'shows the number of entries in the Negative answer cache', ''); +$pmda->add_metric(pmda_pmid(1,12),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.noerror_answers", + 'counts the number of times it answered NOERROR since starting', ''); +$pmda->add_metric(pmda_pmid(1,13),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.nsspeeds_entries", + 'shows the number of entries in the NS speeds map', ''); +$pmda->add_metric(pmda_pmid(1,14),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.nsset_invalidations", + 'number of times an nsset was dropped because it no longer worked', ''); +$pmda->add_metric(pmda_pmid(1,15),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.nxdomain_answers", + 'counts the number of times it answered NXDOMAIN since starting', ''); +$pmda->add_metric(pmda_pmid(1,16),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.outgoing_timeouts", + 'counts the number of timeouts on outgoing UDP queries since starting', ''); +$pmda->add_metric(pmda_pmid(1,17),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.over_capacity_drops", + 'Questions dropped because over maximum concurrent query limit', ''); +$pmda->add_metric(pmda_pmid(1,18),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.packetcache_entries", + 'Size of packet cache', ''); +$pmda->add_metric(pmda_pmid(1,19),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.packetcache_hits", + 'Packet cache hits', ''); +$pmda->add_metric(pmda_pmid(1,20),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.packetcache_misses", + 'Packet cache misses', ''); +$pmda->add_metric(pmda_pmid(1,21),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_USEC,0), + "pdns.recursor.qa_latency", + 'shows the current latency average, in microseconds', ''); +$pmda->add_metric(pmda_pmid(1,22),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.questions", + 'counts all End-user initiated queries with the RD bit set', ''); +$pmda->add_metric(pmda_pmid(1,23),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.resource_limits", + 'counts number of queries that could not be performed because of resource limits', ''); +$pmda->add_metric(pmda_pmid(1,24),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.server_parse_errors", + 'counts number of server replied packets that could not be parsed', ''); +$pmda->add_metric(pmda_pmid(1,25),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.servfail_answers", + 'counts the number of times it answered SERVFAIL since starting', ''); +$pmda->add_metric(pmda_pmid(1,26),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.spoof_prevents", + 'number of times PowerDNS considered itself spoofed, and dropped the data', ''); +$pmda->add_metric(pmda_pmid(1,27),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), + "pdns.recursor.sys_msec", + 'number of CPU milliseconds spent in "system" mode', ''); +$pmda->add_metric(pmda_pmid(1,28),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.tcp_client_overflow", + 'number of times an IP address was denied TCP access because it already had too many connections', ''); +$pmda->add_metric(pmda_pmid(1,29),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.tcp_outqueries", + 'counts the number of outgoing TCP queries since starting', ''); +$pmda->add_metric(pmda_pmid(1,30),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.tcp_questions", + 'counts all incoming TCP queries (since starting)', ''); +$pmda->add_metric(pmda_pmid(1,31),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.throttled_out", + 'counts the number of throttled outgoing UDP queries since starting', ''); +$pmda->add_metric(pmda_pmid(1,32),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), + "pdns.recursor.throttle_entries", + 'shows the number of entries in the throttle map', ''); +$pmda->add_metric(pmda_pmid(1,33),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.unauthorized_tcp", + 'number of TCP questions denied because of allow-from restrictions', ''); +$pmda->add_metric(pmda_pmid(1,34),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.unauthorized_udp", + 'number of UDP questions denied because of allow-from restrictions', ''); +$pmda->add_metric(pmda_pmid(1,35),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.unexpected_packets", + 'number of answers from remote servers that were unexpected (might point to spoofing)', ''); +$pmda->add_metric(pmda_pmid(1,36),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "pdns.recursor.uptime", + 'number of seconds process has been running', ''); +$pmda->add_metric(pmda_pmid(1,37),PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), + "pdns.recursor.user_msec", + 'number of CPU milliseconds spent in "user" mode', ''); + +$pmda->add_indom($recursor_answers_indom, \@recursor_answers_dom, '', ''); +$pmda->set_fetch_callback(\&pdns_fetch_callback); +$pmda->set_fetch(\&pdns_fetch); +$pmda->run; + +=pod + +=head1 NAME + +pmdapdns - PowerDNS performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from the PowerDNS authorative daemon as well as the recursive +resolver. + +=head1 INSTALLATION + +If you want access to the names and values for the PowerDNS performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/pdns + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/pdns + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/pdns/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/pdns/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/pdns.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), pdns_control(8), and rec_control(1). diff --git a/src/pmdas/pmcd/GNUmakefile b/src/pmdas/pmcd/GNUmakefile new file mode 100644 index 0000000..1534531 --- /dev/null +++ b/src/pmdas/pmcd/GNUmakefile @@ -0,0 +1,55 @@ +#!gmake +# +# Copyright (c) 2000-2001,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# pmcd PMDA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +DFILES = help +LTARGETS = help.dir +LDIRT = *.log *.dir *.pag domain.h so_locations + +TARGETS = help.dir +LSRCFILES = help root_pmcd + +SUBDIRS = src +PMDADIR = $(PCP_PMDAS_DIR)/pmcd +CONF_LINE = "pmcd 2 dso pmcd_init $(PCP_PMDAS_DIR)/pmcd/pmda_pmcd.$(DSOSUFFIX)" + +default_pcp default :: $(TARGETS) + +default_pcp default :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install_pcp install :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install_pcp install :: $(SUBDIRS) + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 help.dir help.pag $(PMDADIR) + $(INSTALL) -m 644 root_pmcd $(PCP_VAR_DIR)/pmns/root_pmcd + +help.dir: help root_pmcd + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -v 2 -n root_pmcd -o help < help + +include $(BUILDRULES) diff --git a/src/pmdas/pmcd/help b/src/pmdas/pmcd/help new file mode 100644 index 0000000..402ecf2 --- /dev/null +++ b/src/pmdas/pmcd/help @@ -0,0 +1,533 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# pmcd PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help text goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ 2.1 Instance domain "pmloggers" from PMCD PMDA +This is the list of currently active pmlogger instances on the same +machine as this PMCD. The instance names are the process ids of the +pmlogger instances. The primary pmlogger has an extra instance with the +instance name "primary" and an instance id of zero (in addition to its +normal process id instance). + +@ 2.2 pmcd control register Instance Domain +One instance per pmcd control register. + +The internal instance identifiers are the numbers 0 to 15. +The external instance names are he ASCII equivalent of the internal +instance identifiers. + +@ 2.3 PMDA Instance Domain +One instance per PMDA managed by PMCD. The external and internal instance +identifiers are taken from the first two fields of the PMDA specification +in $PCP_PMCDCONF_PATH. + +@ 2.4 pmie Instance Domain +One instance per running pmie process. The internal and external instance +identifiers are the process ids of the pmie instances. + +@ 2.5 buffer pool Instance Domain +The instances are as follows: + + 1024 1024-byte PDU buffers managed by __pmFindPDUBuf, __pmPinPDUBuf + and __pmUnpinPDUBuf + 2048 2-Kbyte PDU buffers managed by __pmFindPDUBuf, __pmPinPDUBuf + and __pmUnpinPDUBuf + 4096 3-Kbyte or 4-Kbyte PDU buffers managed by __pmFindPDUBuf, + __pmPinPDUBuf and __pmUnpinPDUBuf + 8192 5-Kbyte, 6-Kbyte, 7-Kbyte or 8-Kbyte PDU buffers managed by + __pmFindPDUBuf, __pmPinPDUBuf and __pmUnpinPDUBuf + 8192+ PDU buffers larger that 8-Kbyte managed by __pmFindPDUBuf, + __pmPinPDUBuf and __pmUnpinPDUBuf + +@ pmcd.numagents Number of agents (PMDAs) currently connected to PMCD +The number of agents (PMDAs) currently connected to PMCD. This may differ +from the number of agents configured in $PCP_PMCDCONF_PATH if agents have +terminated and/or been timed-out by PMCD. + +@ pmcd.numclients Number of clients currently connected to PMCD +The number of connections open to client programs retrieving information +from PMCD. + +@ pmcd.datasize Space allocated for PMCD and DSO agents' data segment (K) +This metric returns the amount of memory in kilobytes allocated for the +data segment of PMCD and any DSO agents (PMDAs) that it has loaded. + +This is handy for tracing memory utilization (and leaks) in DSOs during +development. + +@ pmcd.buf.alloc Allocated buffers in internal memory pools +This metric returns the number of allocated buffers for the various buffer +pools used by pmcd. + +This is handy for tracing memory utilization (and leaks) in DSOs during +development. + +@ pmcd.buf.free Free buffers in internal memory pools +This metric returns the number of free buffers for the various buffer +pools used by pmcd. + +This is handy for tracing memory utilization (and leaks) in DSOs during +development. + +@ pmcd.control.timeout Timeout interval for slow/hung agents (PMDAs) +PDU exchanges with agents (PMDAs) managed by PMCD are subject to timeouts +which detect and clean up slow or disfunctional agents. This metric +returns the current timeout period in seconds being used for the agents. +If the value is zero, timeouts are not being used. This corresponds to +the -t option described in the man page, pmcd(1). + +It is possible to store a new timeout value into this metric. Storing zero +will turn off timeouts. Subsequent storing of a non-zero value will turn +on the timeouts again. + +@ pmcd.control.debug Current value of PMCD debug flags +The current value of the PMCD debug flags. This is a bit-wise OR of the +flags described in the output of pmdbg -l. The PMCD-specific flags are: + + DBG_TRACE_APPL0 2048 Trace agent & client I/O and termination + DBG_TRACE_APPL1 4096 Trace host access control + DBG_TRACE_APPL2 8192 Trace config file scanner and parser + +It is possible to store values into this metric. Diagnostic output is +written to the PMCD log file (usually $PCP_LOG_DIR/pmcd/pmcd.log). + +Setting this metric to -1 terminates PMCD. + +@ pmcd.pdu_in.total Total PDUs received by PMCD +Running total of all BINARY mode PDUs received by the PMCD from clients +and agents. + +@ pmcd.pdu_in.error ERROR PDUs received by PMCD +Running total of BINARY mode ERROR PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.result RESULT PDUs received by PMCD +Running total of BINARY mode RESULT PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.profile PROFILE PDUs received by PMCD +Running total of BINARY mode PROFILE PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.fetch FETCH PDUs received by PMCD +Running total of BINARY mode FETCH PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.desc_req DESC_REQ PDUs received by PMCD +Running total of BINARY mode DESC_REQ PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.desc DESC PDUs received by PMCD +Running total of BINARY mode DESC PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.instance_req INSTANCE_REQ PDUs received by PMCD +Running total of BINARY mode INSTANCE_REQ PDUs received by the PMCD +from clients and agents. + +@ pmcd.pdu_in.instance INSTANCE PDUs received by PMCD +Running total of BINARY mode INSTANCE PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.text_req TEXT_REQ PDUs received by PMCD +Running total of BINARY mode TEXT_REQ PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.text TEXT PDUs received by PMCD +Running total of BINARY mode TEXT PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.control_req CONTROL_REQ PDUs received by PMCD +Running total of BINARY mode CONTROL_REQ PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.creds CREDS PDUs received by PMCD +Running total of BINARY mode CREDS PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.pmns_ids PMNS_IDS PDUs received by PMCD +Running total of BINARY mode PMNS_IDS PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.pmns_names PMNS_NAMES PDUs received by PMCD +Running total of BINARY mode PMNS_NAMES PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.pmns_child PMNS_CHILD PDUs received by PMCD +Running total of BINARY mode PMNS_CHILD PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.pmns_traverse PMNS_TRAVERSE PDUs received by PMCD +Running total of BINARY mode PMNS_TRAVERSE PDUs received by the PMCD from +clients and agents. + +@ pmcd.pdu_in.auth AUTH PDUs received by PMCD +Running total of BINARY mode AUTH PDUs received by the PMCD from +clients and agents. These PDUs are used for authentication. + +@ pmcd.pdu_out.total Total PDUs sent by PMCD +Running total of all BINARY mode PDUs sent by the PMCD to clients and +agents. + +@ pmcd.pdu_out.error ERROR PDUs sent by PMCD +Running total of BINARY mode ERROR PDUs sent by the PMCD to clients and +agents. + +@ pmcd.pdu_out.result RESULT PDUs sent by PMCD +Running total of BINARY mode RESULT PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.profile PROFILE PDUs sent by PMCD +Running total of BINARY mode PROFILE PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.fetch FETCH PDUs sent by PMCD +Running total of BINARY mode FETCH PDUs sent by the PMCD to clients and +agents. + +@ pmcd.pdu_out.desc_req DESC_REQ PDUs sent by PMCD +Running total of BINARY mode DESC_REQ PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.desc DESC PDUs sent by PMCD +Running total of BINARY mode DESC PDUs sent by the PMCD to clients and +agents. + +@ pmcd.pdu_out.instance_req INSTANCE_REQ PDUs sent by PMCD +Running total of BINARY mode INSTANCE_REQ PDUs sent by the PMCD to +clients and agents. + +@ pmcd.pdu_out.instance INSTANCE PDUs sent by PMCD +Running total of BINARY mode INSTANCE PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.text_req TEXT_REQ PDUs sent by PMCD +Running total of BINARY mode TEXT_REQ PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.text TEXT PDUs sent by PMCD +Running total of BINARY mode TEXT PDUs sent by the PMCD to clients and +agents. + +@ pmcd.pdu_out.control_req CONTROL_REQ PDUs sent by PMCD +Running total of BINARY mode CONTROL_REQ PDUs sent by the PMCD to +clients and agents. + +@ pmcd.pdu_out.creds CREDS PDUs sent by PMCD +Running total of BINARY mode CREDS PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.pmns_ids PMNS_IDS PDUs sent by PMCD +Running total of BINARY mode PMNS_IDS PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.pmns_names PMNS_NAMES PDUs sent by PMCD +Running total of BINARY mode PMNS_NAMES PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.pmns_child PMNS_CHILD PDUs sent by PMCD +Running total of BINARY mode PMNS_CHILD PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.pmns_traverse PMNS_TRAVERSE PDUs sent by PMCD +Running total of BINARY mode PMNS_TRAVERSE PDUs sent by the PMCD to clients +and agents. + +@ pmcd.pdu_out.auth AUTH PDUs sent by PMCD +Running total of BINARY mode AUTH PDUs sent by the PMCD to clients +and agents. These PDUs are used for authentication. + +@ pmcd.pmlogger.host host where active pmlogger is running +The fully qualified domain name of the host on which a pmlogger +instance is running. + +The instance names are process ids of the active pmloggers. The +primary pmlogger has an extra instance with the instance name "primary" +and an instance id of zero (in addition to its normal process id +instance). + +@ pmcd.pmlogger.port control port for active pmlogger +Each pmlogger instance has a port for receiving log control +information. This metric is a list of the active pmlogger control +ports on the same machine as this PMCD (i.e. the host identified in the +corresponding pmcd.pmlogger.host metric). + +The instance names are process ids of the active pmloggers. The +primary pmlogger has an extra instance with the instance name "primary" +and an instance id of zero (in addition to its normal process id +instance). + +@ pmcd.pmlogger.archive full pathname to archive basename for active pmlogger +The full pathname through the filesystem on the corresponding host +(pmcd.pmlogger.host) that is the base name for the archive log files. + +The instance names are process ids of the active pmloggers. The +primary pmlogger has an extra instance with the instance name "primary" +and an instance id of zero (in addition to its normal process id +instance). + +@ pmcd.pmlogger.pmcd_host host from which active pmlogger is fetching metrics +The fully qualified domain name of the host from which a pmlogger +instance is fetching metrics to be archived. + +The instance names are process ids of the active pmloggers. The +primary pmlogger has an extra instance with the instance name "primary" +and an instance id of zero (in addition to its normal process id +instance). + +@ pmcd.timezone local $TZ +Value for the $TZ environment variable where the PMCD is running. +Enables determination of "local" time for timestamps returned via +PMCD from a remote host. + +@ pmcd.hostname local hostname +A reasonably unique identifier of the PMCD installation, for use +by pmlogger or other tools to identify the source principal of +the data (as distinct from identifying the connection/protocol +used to reach it). + +@ pmcd.simabi Procedure call model and ABI version of this PMCD +SIM is the subprogram interface model (originally from the MIPS object +code formats), and ABI is the application binary interface. Both +relate to the way the PMCD binary was compiled and linked. + +Usually DSO PMDAs must be compiled and linked in the same way before +they can be used with PMCD. + +On some platforms this metric is not available. + +@ pmcd.version PMCD version + +@ pmcd.control.register a vector of registers that may be set by users +A vector of 16 32-bit registers that are identified by the instance +identifiers 0 through 15. + +The register contents are initially zero, but may be subsequently +modified to be an arbitrary value using pmStore(3) or pmstore(1). + +The values are not used internally, but rather act as a repository into +which operational information might be stored, and then exported to +modify the behavior of client programs, e.g. inhibit pmie(1) rule +firing, or trigger a status indicator. In this way, +pmcd.control.register acts like a primitive bulletin board. + +Example use might be as follows + register[0] telephone no. of person assigned to current system problem + register[1] telephone no. of person assigned to current network problem + register[2] ORACLE database is down + register[3] backup in progress + register[4] shopping days to Christmas + +@ pmcd.control.traceconn control PMCD connection event tracing +Set to 1 to enable PMCD event tracing for all connection-related +events for clients and PMDAs. + +Set to 0 to disable PMCD connection event tracing. + +@ pmcd.control.tracepdu control PMCD PDU event tracing +Set to 1 to enable PMCD event tracing for all PDUs sent and received +by PMCD. + +Set to 0 to disable PMCD PDU event tracing. + +@ pmcd.control.tracenobuf control buffering of PMCD event tracing +Set to 1 to enable unbuffered PMCD event tracing, where each event is +reported as it happens. + +Set to 0 to enable buffering of PMCD event traces (this is the default), +and event traces will only be dumped or reported when an error occurs or +a value is stored into the PCP metric pmcd.control.dumptrace. + +@ pmcd.control.tracebufs number of buffers for PMCD event tracing +Defaults to 20. May be changed dynamically. + +@ pmcd.control.dumptrace force dump of PMCD event tracing buffers +Storing any value into this metric causes the PMCD event trace buffers to +be dumped to PMCD's log file. + +@ pmcd.control.sighup force PMCD reset via SIGHUP +Storing any value into this metric causes PMCD to be reset by sending +itself a SIGHUP signal. + +On reset (either by storing into pmcd.control.sighup or by sending PMCD a +SIGHUP directly), PMCD will restart any failed PMDAs and reload the PMNS +if it has been changed. + +@ pmcd.control.dumpconn force dump of PMCD client connections +Storing any value into this metric causes the details of the current PMCD +client connections to be dumped to PMCD's log file. + +@ pmcd.agent.type PMDA type +From $PCP_PMCDCONF_PATH, this metric encodes the PMDA type as follows: + (x << 1) | y +where "x" is the IPC type between PMCD and the PMDA, i.e. 0 for DSO, 1 +for socket or 2 for pipe, and "y" is the message passing style, i.e. +0 for binary or 1 for ASCII. + +@ pmcd.agent.status PMDA status +This metric encodes the current status of each PMDA. The default value +is 0 if the PMDA is active. + +Other values encode various degrees of PMDA difficulty in three bit fields +(bit 0 is the low-order bit) as follows: + +bits 7..0 + 1 the PMDA is connected, but not yet "ready" to accept requests + from the PMDA + 2 the PMDA has exited of its own accord + 4 some error prevented the PMDA being started + 8 PMCD stopped communication with the PMDA due to a protocol or + timeout error + +bits 15..8 + the exit() status from the PMDA + +bits 23..16 + the number of the signal that terminated the PMDA + +@ pmcd.services running PCP services on the local host +A space-separated string representing all running PCP services with PID +files in $PCP_RUN_DIR (such as pmcd itself, pmproxy and a few others). + +@ pmcd.openfds highest PMCD file descriptor +The highest file descriptor index used by PMCD for a Client or PMDA +connection. + +@ pmcd.pmie.numrules number of rules being evaluated +The total number of rules being evaluated by each pmie process. + +@ pmcd.pmie.eval.true count of pmie predicates evaluated to true +The predicate part of a pmie rule can be said to evaluate to either true, +false, or not known. This metric is a cumulative count of the number of +rules which have evaluated to true for each pmie instance. + +@ pmcd.pmie.eval.false count of pmie predicates evaluated to false +The predicate part of a pmie rule can be said to evaluate to either true, +false, or not known. This metric is a cumulative count of the number of +rules which have evaluated to false for each pmie instance. + +@ pmcd.pmie.eval.unknown count of pmie predicates not evaluated +The predicate part of a pmie rule can be said to evaluate to either true, +false, or not known. This metric is a cumulative count of the number of +rules which have not been successfully evaluated. This could be due to not +yet having sufficient values to evaluate the rule, or a metric fetch may +have been unsuccessful in retrieving current values for metrics required +for evaluation of the rule. + +@ pmcd.pmie.eval.expected expected rate of rule evaluations +This is the expected rate of evaluation of pmie rules. The value is +calculated once when pmie starts, and is the number of pmie rules divided +by the average time interval over which they are to be evaluated. + +@ pmcd.pmie.eval.actual count of actual rule evaluations +A cumulative count of the pmie rules which have been evaluated. + +This value is incremented once for each evaluation of each rule. + +@ pmcd.pmie.actions count of rules evaluating to true +A cumulative count of the evaluated pmie rules which have evaluated to true. + +This value is incremented once each time an action is executed. This value +will always be less than or equal to pmcd.pmie.eval.true because predicates +which have evaluated to true may be suppressed in the action part of the +pmie rule, in which case this counter will not be incremented. + +@ pmcd.pmie.configfile configuration file name +The full path in the filesystem to the configuration file containing the +rules being evaluated by each pmie instance. + +If the configuration file was supplied on the standard input, then this +metric will have the value "". If multiple configuration files were +given to pmie, then the value of this metric will be the first configuration +file specified. + +@ pmcd.pmie.pmcd_host default hostname for pmie instance +The default host from which pmie is fetching metrics. This is either the +hostname given to pmie on the command line or the local host. Note that this +does not consider host names specified in the pmie configuration file (these +are considered non-default and can be more than one per pmie instance). +All daemon pmie instances started through pmie_check(1) will have their +default host passed in on their command line. + +@ pmcd.pmie.logfile filename of pmie instance event log +The file to which each instance of pmie is writting events. No two pmie +instances can share the same log file. If no logfile was specified when +pmie was started, this metrics has the value "". All daemon pmie +instances started through pmie_check(1) must have an associated log file. + +@ pmcd.build build version for installed PCP package +Minor part of the PCP build version numbering. For example on Linux +with RPM packaging, if the PCP RPM version is pcp-2.5.99-20070323 then +pmcd.build returns the string "20070323". + +@ pmcd.client.whoami optional identification information for clients of pmcd +This metric is defined over an instance domain containing one entry +per active client of pmcd. The instance number is a sequence number +for each client (restarts at 0 each time pmcd is restarted). The value +of the metric by default is the IP address of the client. + +Clients can optionally use pmStore to modify their own "whoami" string +to provide more useful information about the client. + +@ pmcd.client.start_date date and time client connected to pmcd +The date and time in ctime(2) format on which the client connected +to pmcd. + +@ pmcd.cputime.total CPU time used by pmcd and DSO PMDAs +Sum of user and system time since pmcd started. + +@ pmcd.cputime.per_pdu_in average CPU time per PDU received by pmcd +When first requested it is the average since pmcd started, so +pmcd.cputime.total divided by pmcd.pdu_in.total. + +Subsequent fetches by a PMAPI client will return the average CPU +time per PDU received by pmcd (for all clients) since the last time +the PMAPI client fetched this metric. + +@ pmcd.feature.secure status of secure_sockets protocol feature in pmcd +A value of zero indicates no support, one indicates actively available +(including configuration and validity of the server side certificates). + +@ pmcd.feature.compress status of protocol compression feature in pmcd +A value of zero indicates no support, one indicates actively available. + +@ pmcd.feature.ipv6 status of Internet Protocol Version 6 support in pmcd +A value of zero indicates no support, one indicates actively available. + +@ pmcd.feature.authentication status of per-user authentication support +A value of zero indicates no support, one indicates actively available. + +@ pmcd.feature.creds_required status of required credentials support +A value of zero indicates no support, one indicates actively available. + +@ pmcd.feature.unix_domain_sockets status of unix domain socket support +A value of zero indicates no support, one indicates actively available. + +@ pmcd.feature.service_discovery status of service advertising and discovery +A value of zero indicates no support, one indicates actively available. diff --git a/src/pmdas/pmcd/root_pmcd b/src/pmdas/pmcd/root_pmcd new file mode 100644 index 0000000..010df41 --- /dev/null +++ b/src/pmdas/pmcd/root_pmcd @@ -0,0 +1,153 @@ +/* + * PMCD metrics name space + */ + +root { + pmcd +} + +/* + * the domain for the pmcd PMDA ... + */ +#ifndef PMCD +#define PMCD 2 +#endif + +pmcd { + control + pdu_in + pdu_out + datasize PMCD:0:1 + numagents PMCD:0:2 + agent + numclients PMCD:0:3 + pmlogger + timezone PMCD:0:5 + simabi PMCD:0:6 + version PMCD:0:7 + services PMCD:0:16 + openfds PMCD:0:17 + build PMCD:0:20 + hostname PMCD:0:21 + pmie + buf + client + cputime + feature +} + +pmcd.control { + debug PMCD:0:0 + timeout PMCD:0:4 + register PMCD:0:8 + traceconn PMCD:0:9 + tracepdu PMCD:0:10 + tracenobuf PMCD:0:14 + tracebufs PMCD:0:11 + dumptrace PMCD:0:12 + dumpconn PMCD:0:13 + sighup PMCD:0:15 +} + +/* + * Note: strange numbering for pmcd.pdu_{in,out}.total for + * compatibility with earlier PCP versions + */ + +pmcd.pdu_in { + error PMCD:1:0 + result PMCD:1:1 + profile PMCD:1:2 + fetch PMCD:1:3 + desc_req PMCD:1:4 + desc PMCD:1:5 + instance_req PMCD:1:6 + instance PMCD:1:7 + text_req PMCD:1:8 + text PMCD:1:9 + control_req PMCD:1:10 + creds PMCD:1:12 + pmns_ids PMCD:1:13 + pmns_names PMCD:1:14 + pmns_child PMCD:1:15 + total PMCD:1:16 + pmns_traverse PMCD:1:17 + auth PMCD:1:18 +} + +pmcd.pdu_out { + error PMCD:2:0 + result PMCD:2:1 + profile PMCD:2:2 + fetch PMCD:2:3 + desc_req PMCD:2:4 + desc PMCD:2:5 + instance_req PMCD:2:6 + instance PMCD:2:7 + text_req PMCD:2:8 + text PMCD:2:9 + control_req PMCD:2:10 + creds PMCD:2:12 + pmns_ids PMCD:2:13 + pmns_names PMCD:2:14 + pmns_child PMCD:2:15 + total PMCD:2:16 + pmns_traverse PMCD:2:17 + auth PMCD:2:18 +} + +pmcd.pmlogger { + host PMCD:3:3 + port PMCD:3:0 + archive PMCD:3:2 + pmcd_host PMCD:3:1 +} + +pmcd.agent { + type PMCD:4:0 + status PMCD:4:1 +} + +pmcd.pmie { + configfile PMCD:5:0 + logfile PMCD:5:1 + pmcd_host PMCD:5:2 + numrules PMCD:5:3 + actions PMCD:5:4 + eval +} + +pmcd.pmie.eval { + true PMCD:5:5 + false PMCD:5:6 + unknown PMCD:5:7 + expected PMCD:5:8 + actual PMCD:5:9 +} + +pmcd.buf { + alloc PMCD:0:18 + free PMCD:0:19 +} + +pmcd.client { + whoami PMCD:6:0 + start_date PMCD:6:1 +} + +pmcd.cputime { + total PMCD:7:0 + per_pdu_in PMCD:7:1 +} + +pmcd.feature { + secure PMCD:8:0 + compress PMCD:8:1 + ipv6 PMCD:8:2 + authentication PMCD:8:3 + creds_required PMCD:8:4 + unix_domain_sockets PMCD:8:5 + service_discovery PMCD:8:6 +} + +#undef PMCD diff --git a/src/pmdas/pmcd/src/GNUmakefile b/src/pmdas/pmcd/src/GNUmakefile new file mode 100644 index 0000000..ff5b4ff --- /dev/null +++ b/src/pmdas/pmcd/src/GNUmakefile @@ -0,0 +1,68 @@ +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +LIBTARGET = pmda_pmcd.$(DSOSUFFIX) +PMDAINIT = pmcd_init + +CFILES = pmcd.c +LSRCFILES = objstyle +VERSION_SCRIPT = exports +LDIRT = $(VERSION_SCRIPT) + +# Add to CFLAGS to find files in pmcd/src... +LCFLAGS = -I$(TOPDIR)/src +LCFLAGS += -I$(TOPDIR)/src/pmie/src + +ifneq (, $(filter linux kfreebsd gnu, $(TARGET_OS))) +ABI = $(shell ./objstyle) +LCFLAGS += -DSIM_ABI=\""$(ABI)"\" +endif +ifeq "$(TARGET_OS)" "darwin" +ABI = $(shell ./objstyle) +LCFLAGS += -DSIM_ABI=\"$(ABI)\" +endif +ifdef PACKAGE_BUILD +BUILD = $(PACKAGE_BUILD) +else +BUILD = unknown +endif + +LCFLAGS += -DBUILD=\"$(BUILD)\" +LCFLAGS += $(INVISIBILITY) + +LLDLIBS = $(PCP_PMDALIB) +ifeq "$(TARGET_OS)" "mingw" +LLDLIBS += -lpcp_pmcd +PCPLIB_LDFLAGS += -L$(TOPDIR)/src/libpcp_pmcd/src +endif + +default: $(LIBTARGET) + +install: default + $(INSTALL) -m 755 -d $(PCP_PMDAS_DIR)/pmcd + $(INSTALL) -m 755 $(LIBTARGET) $(PCP_PMDAS_DIR)/pmcd/$(LIBTARGET) + +include $(BUILDRULES) + +default_pcp : default + +install_pcp : install + +$(LIBTARGET): $(VERSION_SCRIPT) + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) diff --git a/src/pmdas/pmcd/src/objstyle b/src/pmdas/pmcd/src/objstyle new file mode 100755 index 0000000..5929bab --- /dev/null +++ b/src/pmdas/pmcd/src/objstyle @@ -0,0 +1,88 @@ +#! /bin/sh +# +# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# identify the objstyle of linux platforms other than ia32 and ia64. + +echo 'int main(){return 0;}' >dummy.c +cc -c dummy.c + +# we've had bad experience with file(1) and compiled "magic" files +# on assorted Linux versions ... try to use the uncompiled magic +# file if possible +# +# if you need to modify this, make consistent changes in +# src/pmdas/pmcd/src/objstyle +# qa/605 +# +magic='' +for file in /usr/share/misc/magic /usr/share/file/magic /usr/share/magic \ + /etc/magic +do + if [ -f "$file" ] + then + # found a file, check it contains some definitions ... + nl=`sed -e '/^#/d' -e '/^[ ]*$/d' <"$file" | wc -l | sed -e 's/ //g'` + if [ "$nl" -gt 0 ] + then + magic=$file + break + fi + fi +done + +# sample file output +# +# ia32 laptop +# dummy.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped +# +# Mac OS X +# pmcd.o: Mach-O universal binary with 2 architectures +# pmcd.o (for architecture i386): Mach-O object i386 +# pmcd.o (for architecture x86_64): Mach-O 64-bit object x86_64 +# +# dummy.o: Mach-O 64-bit object x86_64 +# +# SLES10 +# dummy.o: ELF 64-bit LSB relocatable, IA-64 (Intel 64 bit architecture), version 1 (SYSV), not stripped +# + +if [ -n "$magic" ] +then + file -m $magic dummy.o +else + file dummy.o +fi \ +| ( sed -n \ + -e '/ ELF /{ +s/^[^,]*, // +s/, .*// +s/ *([^)]*)// +s/ // +s/x86-64/x86_64/ +p +}' \ + -e '/object/{ +s/[ ]*$// +s/.*[ ]// +p +}' \ +| tr '[\012]' '[+]' \ +; echo ) \ +| sed -e 's/+$//' + +rm -f dummy.[co] diff --git a/src/pmdas/pmcd/src/pmcd.c b/src/pmdas/pmcd/src/pmcd.c new file mode 100644 index 0000000..fecfd28 --- /dev/null +++ b/src/pmdas/pmcd/src/pmcd.c @@ -0,0 +1,1869 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 1995-2001,2003,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "stats.h" +#include "pmcd/src/pmcd.h" +#include "pmcd/src/client.h" +#include +#if defined(IS_SOLARIS) +#include +#endif + +/* + * Note: strange numbering for pmcd.pdu_{in,out}.total for + * compatibility with earlier PCP versions ... this is the "item" + * field of the PMID + */ +#define _TOTAL 16 + +/* + * all metrics supported in this PMD - one table entry for each + */ +static pmDesc desctab[] = { +/* control.debug */ + { PMDA_PMID(0,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* datasize */ + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* numagents */ + { PMDA_PMID(0,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* numclients */ + { PMDA_PMID(0,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.timeout */ + { PMDA_PMID(0,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* timezone -- local $TZ -- for pmlogger */ + { PMDA_PMID(0,5), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* simabi -- Subprogram Interface Model, ABI version of this pmcd (normally PM_TYPE_STRING) */ + { PMDA_PMID(0,6), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* version -- pcp version */ + { PMDA_PMID(0,7), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.register -- bulletin board */ + { PMDA_PMID(0,8), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.traceconn -- trace connections */ + { PMDA_PMID(0,9), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.tracepdu -- trace PDU traffic */ + { PMDA_PMID(0,10), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.tracebufs -- number of trace buffers */ + { PMDA_PMID(0,11), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.dumptrace -- push-button, pmStore to dump trace */ + { PMDA_PMID(0,12), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.dumpconn -- push-button, pmStore to dump connections */ + { PMDA_PMID(0,13), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.tracenobuf -- unbuffered tracing */ + { PMDA_PMID(0,14), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* control.sighup -- push-button, pmStore to SIGHUP pmcd */ + { PMDA_PMID(0,15), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* services -- locally running PCP services */ + { PMDA_PMID(0,16), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* openfds -- number of open file descriptors */ + { PMDA_PMID(0,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* buf.alloc */ + { PMDA_PMID(0,18), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* buf.free */ + { PMDA_PMID(0,19), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* build -- pcp build number */ + { PMDA_PMID(0,20), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* hostname -- local hostname -- for pmlogger */ + { PMDA_PMID(0,21), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, + +/* pdu_in.error */ + { PMDA_PMID(1,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.result */ + { PMDA_PMID(1,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.profile */ + { PMDA_PMID(1,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.fetch */ + { PMDA_PMID(1,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.desc_req */ + { PMDA_PMID(1,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.desc */ + { PMDA_PMID(1,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.instance_req */ + { PMDA_PMID(1,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.instance */ + { PMDA_PMID(1,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.text_req */ + { PMDA_PMID(1,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.text */ + { PMDA_PMID(1,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.control_req */ + { PMDA_PMID(1,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.creds */ + { PMDA_PMID(1,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.pmns_ids */ + { PMDA_PMID(1,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.pmns_names */ + { PMDA_PMID(1,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.pmns_child */ + { PMDA_PMID(1,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.total */ + { PMDA_PMID(1,_TOTAL), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.pmns_traverse */ + { PMDA_PMID(1,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_in.auth */ + { PMDA_PMID(1,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, + +/* pdu_out.error */ + { PMDA_PMID(2,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.result */ + { PMDA_PMID(2,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.profile */ + { PMDA_PMID(2,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.fetch */ + { PMDA_PMID(2,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.desc_req */ + { PMDA_PMID(2,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.desc */ + { PMDA_PMID(2,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.instance_req */ + { PMDA_PMID(2,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.instance */ + { PMDA_PMID(2,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.text_req */ + { PMDA_PMID(2,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.text */ + { PMDA_PMID(2,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.control_req */ + { PMDA_PMID(2,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.creds */ + { PMDA_PMID(2,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.pmns_ids */ + { PMDA_PMID(2,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.pmns_names */ + { PMDA_PMID(2,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.pmns_child */ + { PMDA_PMID(2,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.total */ + { PMDA_PMID(2,_TOTAL), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.pmns_traverse */ + { PMDA_PMID(2,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pdu_out.auth */ + { PMDA_PMID(2,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, + +/* pmlogger.port */ + { PMDA_PMID(3,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmlogger.pmcd_host */ + { PMDA_PMID(3,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmlogger.archive */ + { PMDA_PMID(3,2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmlogger.host */ + { PMDA_PMID(3,3), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, + +/* agent.type */ + { PMDA_PMID(4,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* agent.status */ + { PMDA_PMID(4,1), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, + +/* pmie.configfile */ + { PMDA_PMID(5,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmie.logfile */ + { PMDA_PMID(5,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmie.pmcd_host */ + { PMDA_PMID(5,2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmie.numrules */ + { PMDA_PMID(5,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmie.actions */ + { PMDA_PMID(5,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pmie.eval.true */ + { PMDA_PMID(5,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pmie.eval.false */ + { PMDA_PMID(5,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pmie.eval.unknown */ + { PMDA_PMID(5,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* pmie.eval.expected */ + { PMDA_PMID(5,8), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE) }, +/* pmie.eval.actual */ + { PMDA_PMID(5,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, + +/* client.whoami */ + { PMDA_PMID(6,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* client.start_date */ + { PMDA_PMID(6,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, + +/* pmcd.cputime.total */ + { PMDA_PMID(7,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, +/* pmcd.cputime.per_pdu_in */ + { PMDA_PMID(7,1), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,1,-1,0,PM_TIME_USEC,PM_COUNT_ONE) }, + +/* pmcd.feature.secure */ + { PMDA_PMID(8,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmcd.feature.compress */ + { PMDA_PMID(8,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmcd.feature.ipv6 */ + { PMDA_PMID(8,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmcd.feature.authentication */ + { PMDA_PMID(8,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmcd.feature.creds_required */ + { PMDA_PMID(8,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmcd.feature.unix_domain_sockets */ + { PMDA_PMID(8,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pmcd.feature.service_discovery */ + { PMDA_PMID(8,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, + +/* End-of-List */ + { PM_ID_NULL, 0, 0, 0, PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } +}; +static int ndesc = sizeof(desctab)/sizeof(desctab[0]); + +static __pmProfile *_profile; /* last received profile */ + +/* there are four instance domains: pmlogger, register, PMDA, and pmie */ +#define INDOM_PMLOGGERS 1 +static pmInDom logindom; +#define INDOM_REGISTER 2 +static pmInDom regindom; +#define INDOM_PMDAS 3 +static pmInDom pmdaindom; +#define INDOM_PMIES 4 +static pmInDom pmieindom; +#define INDOM_POOL 5 +static pmInDom bufindom; +#define INDOM_CLIENT 6 +static pmInDom clientindom; + +#define NUMREG 16 +static int reg[NUMREG]; + +typedef struct { + pid_t pid; + int size; + char *name; + void *mmap; +} pmie_t; +static pmie_t *pmies; +static unsigned int npmies; + +static struct { + int inst; + char *iname; +} bufinst[] = { + { 12, "0012" }, + { 20, "0020" }, + { 1024, "1024" }, + { 2048, "2048" }, + { 4196, "4196" }, + { 8192, "8192" }, + { 8193, "8192+" }, +}; +static int nbufsz = sizeof(bufinst) / sizeof(bufinst[0]); + +typedef struct { + int id; /* index into client[] */ + int seq; + char *value; +} whoami_t; +static whoami_t *whoamis; +static unsigned int nwhoamis; + +typedef struct { + int state; + double last_cputime; + __uint64_t last_pdu_in; +} perctx_t; + +/* values for per context state */ +#define CTX_INACTIVE 0 +#define CTX_ACTIVE 1 + +static perctx_t *ctxtab = NULL; +static int num_ctx = 0; + +/* + * expand and initialize the per client context table + */ +static void +grow_ctxtab(int ctx) +{ + ctxtab = (perctx_t *)realloc(ctxtab, (ctx+1)*sizeof(ctxtab[0])); + if (ctxtab == NULL) { + __pmNoMem("grow_ctxtab", (ctx+1)*sizeof(ctxtab[0]), PM_FATAL_ERR); + /*NOTREACHED*/ + } + while (num_ctx <= ctx) { + ctxtab[num_ctx].state = CTX_INACTIVE; + num_ctx++; + } + ctxtab[ctx].state = CTX_INACTIVE; +} + +/* + * this routine is called at initialization to patch up any parts of the + * desctab that cannot be statically initialized, and to optionally + * modify our Performance Metrics Domain Id (dom) + */ +static void +init_tables(int dom) +{ + int i; + __pmID_int *pmidp; + __pmInDom_int *indomp; + + /* set domain in instance domain correctly */ + indomp = (__pmInDom_int *)&logindom; + indomp->flag = 0; + indomp->domain = dom; + indomp->serial = INDOM_PMLOGGERS; + indomp = (__pmInDom_int *)®indom; + indomp->flag = 0; + indomp->domain = dom; + indomp->serial = INDOM_REGISTER; + indomp = (__pmInDom_int *)&pmdaindom; + indomp->flag = 0; + indomp->domain = dom; + indomp->serial = INDOM_PMDAS; + indomp = (__pmInDom_int *)&pmieindom; + indomp->flag = 0; + indomp->domain = dom; + indomp->serial = INDOM_PMIES; + indomp = (__pmInDom_int *)&bufindom; + indomp->flag = 0; + indomp->domain = dom; + indomp->serial = INDOM_POOL; + indomp = (__pmInDom_int *)&clientindom; + indomp->flag = 0; + indomp->domain = dom; + indomp->serial = INDOM_CLIENT; + + /* merge performance domain id part into PMIDs in pmDesc table */ + for (i = 0; desctab[i].pmid != PM_ID_NULL; i++) { + pmidp = (__pmID_int *)&desctab[i].pmid; + pmidp->domain = dom; + if (pmidp->cluster == 0 && pmidp->item == 8) + desctab[i].indom = regindom; + else if (pmidp->cluster == 0 && (pmidp->item == 18 || pmidp->item == 19)) + desctab[i].indom = bufindom; + else if (pmidp->cluster == 3) + desctab[i].indom = logindom; + else if (pmidp->cluster == 4) + desctab[i].indom = pmdaindom; + else if (pmidp->cluster == 5) + desctab[i].indom = pmieindom; + else if (pmidp->cluster == 6) + desctab[i].indom = clientindom; + } + ndesc--; +} + + +static int +pmcd_profile(__pmProfile *prof, pmdaExt *pmda) +{ + _profile = prof; + return 0; +} + +static void +remove_pmie_indom(void) +{ + int n; + + for (n = 0; n < npmies; n++) { + free(pmies[n].name); + __pmMemoryUnmap(pmies[n].mmap, pmies[n].size); + } + free(pmies); + pmies = NULL; + npmies = 0; +} + +static int +stat_time_differs(struct stat *statbuf, struct stat *lastsbuf) +{ +#if defined(HAVE_ST_MTIME_WITH_E) && defined(HAVE_STAT_TIME_T) + if (statbuf->st_mtime != lastsbuf->st_mtime) + return 1; +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + if ((statbuf->st_mtimespec.tv_sec != lastsbuf->st_mtimespec.tv_sec) || + (statbuf->st_mtimespec.tv_nsec != lastsbuf->st_mtimespec.tv_nsec)) + return 1; +#elif defined(HAVE_STAT_TIMESTRUC) || defined(HAVE_STAT_TIMESPEC) || defined(HAVE_STAT_TIMESPEC_T) + if ((statbuf->st_mtim.tv_sec != lastsbuf->st_mtim.tv_sec) || + (statbuf->st_mtim.tv_nsec != lastsbuf->st_mtim.tv_nsec)) + return 1; +#else +!bozo! +#endif + return 0; +} + +/* use a static timestamp, stat PMIE_SUBDIR, if changed update "pmies" */ +static unsigned int +refresh_pmie_indom(void) +{ + static struct stat lastsbuf; + pid_t pmiepid; + struct dirent *dp; + struct stat statbuf; + size_t size; + char *endp; + char fullpath[MAXPATHLEN]; + void *ptr; + DIR *pmiedir; + int fd; + int sep = __pmPathSeparator(); + + snprintf(fullpath, sizeof(fullpath), "%s%c%s", + pmGetConfig("PCP_TMP_DIR"), sep, PMIE_SUBDIR); + if (stat(fullpath, &statbuf) == 0) { + if (stat_time_differs(&statbuf, &lastsbuf)) { + + lastsbuf = statbuf; + + /* tear down the old instance domain */ + if (pmies) + remove_pmie_indom(); + + /* open the directory iterate through mmaping as we go */ + if ((pmiedir = opendir(fullpath)) == NULL) { + __pmNotifyErr(LOG_ERR, "pmcd pmda cannot open %s: %s", + fullpath, osstrerror()); + return 0; + } + /* NOTE: all valid files are already mmapped by pmie */ + while ((dp = readdir(pmiedir)) != NULL) { + size = (npmies+1) * sizeof(pmie_t); + pmiepid = (pid_t)strtoul(dp->d_name, &endp, 10); + if (*endp != '\0') /* skips over "." and ".." here */ + continue; + if (!__pmProcessExists(pmiepid)) + continue; + snprintf(fullpath, sizeof(fullpath), "%s%c%s%c%s", + pmGetConfig("PCP_TMP_DIR"), sep, PMIE_SUBDIR, sep, + dp->d_name); + if (stat(fullpath, &statbuf) < 0) { + __pmNotifyErr(LOG_WARNING, "pmcd pmda cannot stat %s: %s", + fullpath, osstrerror()); + continue; + } + if (statbuf.st_size != sizeof(pmiestats_t)) + continue; + if ((endp = strdup(dp->d_name)) == NULL) { + __pmNoMem("pmie iname", strlen(dp->d_name), PM_RECOV_ERR); + continue; + } + if ((pmies = (pmie_t *)realloc(pmies, size)) == NULL) { + __pmNoMem("pmie instlist", size, PM_RECOV_ERR); + free(endp); + continue; + } + if ((fd = open(fullpath, O_RDONLY)) < 0) { + __pmNotifyErr(LOG_WARNING, "pmcd pmda cannot open %s: %s", + fullpath, osstrerror()); + free(endp); + continue; + } + ptr = __pmMemoryMap(fd, statbuf.st_size, 0); + close(fd); + if (ptr == NULL) { + __pmNotifyErr(LOG_ERR, "pmcd pmda memmap of %s failed: %s", + fullpath, osstrerror()); + free(endp); + continue; + } + else if (((pmiestats_t *)ptr)->version != 1) { + __pmNotifyErr(LOG_WARNING, "incompatible pmie version: %s", + fullpath); + __pmMemoryUnmap(ptr, statbuf.st_size); + free(endp); + continue; + } + pmies[npmies].pid = pmiepid; + pmies[npmies].name = endp; + pmies[npmies].size = statbuf.st_size; + pmies[npmies].mmap = ptr; + npmies++; + } + closedir(pmiedir); + } + } + else { + remove_pmie_indom(); + } + setoserror(0); + return npmies; +} + +static int +pmcd_instance_reg(int inst, char *name, __pmInResult **result) +{ + __pmInResult *res; + int i; + char idx[3]; /* ok for NUMREG <= 99 */ + + res = (__pmInResult *)malloc(sizeof(__pmInResult)); + if (res == NULL) + return -oserror(); + + if (name == NULL && inst == PM_IN_NULL) + res->numinst = NUMREG; + else + res->numinst = 1; + + if (inst == PM_IN_NULL) { + if ((res->instlist = (int *)malloc(res->numinst * sizeof(res->instlist[0]))) == NULL) { + free(res); + return -oserror(); + } + } + else + res->instlist = NULL; + + if (name == NULL) { + if ((res->namelist = (char **)malloc(res->numinst * sizeof(res->namelist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + for (i = 0; i < res->numinst; i++) + res->namelist[0] = NULL; + } + else + res->namelist = NULL; + + if (name == NULL && inst == PM_IN_NULL) { + /* return inst and name for everything */ + for (i = 0; i < res->numinst; i++) { + res->instlist[i] = i; + snprintf(idx, sizeof(idx), "%d", i); + if ((res->namelist[i] = strdup(idx)) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + } + } + else if (name == NULL) { + /* given an inst, return the name */ + if (0 <= inst && inst < NUMREG) { + snprintf(idx, sizeof(idx), "%d", inst); + if ((res->namelist[0] = strdup(idx)) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + } + else { + __pmFreeInResult(res); + return PM_ERR_INST; + } + } + else if (inst == PM_IN_NULL) { + /* given a name, return an inst */ + char *endp; + i = (int)strtol(name, &endp, 10); + if (*endp == '\0' && 0 <= i && i < NUMREG) + res->instlist[0] = i; + else { + __pmFreeInResult(res); + return PM_ERR_INST; + } + } + + *result = res; + return 0; +} + +static int +pmcd_instance_pool(int inst, char *name, __pmInResult **result) +{ + __pmInResult *res; + int i; + + res = (__pmInResult *)malloc(sizeof(__pmInResult)); + if (res == NULL) + return -oserror(); + + if (name == NULL && inst == PM_IN_NULL) + res->numinst = nbufsz; + else + res->numinst = 1; + + if (inst == PM_IN_NULL) { + if ((res->instlist = (int *)malloc(res->numinst * sizeof(res->instlist[0]))) == NULL) { + free(res); + return -oserror(); + } + } + else + res->instlist = NULL; + + if (name == NULL) { + if ((res->namelist = (char **)malloc(res->numinst * sizeof(res->namelist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + for (i = 0; i < res->numinst; i++) + res->namelist[0] = NULL; + } + else + res->namelist = NULL; + + if (name == NULL && inst == PM_IN_NULL) { + /* return inst and name for everything */ + for (i = 0; i < nbufsz; i++) { + res->instlist[i] = bufinst[i].inst; + if ((res->namelist[i] = strdup(bufinst[i].iname)) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + } + } + else if (name == NULL) { + /* given an inst, return the name */ + for (i = 0; i < nbufsz; i++) { + if (inst == bufinst[i].inst) { + if ((res->namelist[0] = strdup(bufinst[i].iname)) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + break; + } + } + if (i == nbufsz) { + __pmFreeInResult(res); + return PM_ERR_INST; + } + } + else if (inst == PM_IN_NULL) { + /* given a name, return an inst */ + for (i = 0; i < nbufsz; i++) { + if (strcmp(name, bufinst[i].iname) == 0) { + res->instlist[0] = bufinst[i].inst; + break; + } + } + if (i == nbufsz) { + __pmFreeInResult(res); + return PM_ERR_INST; + } + } + + *result = res; + return 0; +} + +static int +pmcd_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + int sts = 0; + __pmInResult *res; + int getall = 0; + int getname = 0; /* initialize to pander to gcc */ + int nports = 0; /* initialize to pander to gcc */ + __pmLogPort *ports; + unsigned int pmiecount = 0; /* initialize to pander to gcc */ + int i; + + if (indom == regindom) + return pmcd_instance_reg(inst, name, result); + else if (indom == bufindom) + return pmcd_instance_pool(inst, name, result); + else if (indom == logindom || indom == pmdaindom || indom == pmieindom || indom == clientindom) { + res = (__pmInResult *)malloc(sizeof(__pmInResult)); + if (res == NULL) + return -oserror(); + res->instlist = NULL; + res->namelist = NULL; + + if (indom == logindom) { + /* use the wildcard behaviour of __pmLogFindPort to find + * all pmlogger ports on localhost. Note that + * __pmLogFindPort will not attempt to contact pmcd if + * localhost is specified---this means we don't get a + * recursive call to pmcd which would hang! + */ + if ((nports = __pmLogFindPort("localhost", PM_LOG_ALL_PIDS, &ports)) < 0) { + free(res); + return nports; + } + } + else if (indom == pmieindom) + pmiecount = refresh_pmie_indom(); + + if (name == NULL && inst == PM_IN_NULL) { + getall = 1; + + if (indom == logindom) + res->numinst = nports; + else if (indom == pmdaindom) + res->numinst = nAgents; + else if (indom == pmieindom) + res->numinst = pmiecount; + else if (indom == clientindom) { + res->numinst = 0; + for (i = 0; i < nClients; i++) { + if (client[i].status.connected) res->numinst++; + } + } + } + else { + getname = name == NULL; + res->numinst = 1; + } + + if (getall || !getname) { + if ((res->instlist = (int *)malloc(res->numinst * sizeof(int))) == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance instlist", res->numinst * sizeof(int), PM_RECOV_ERR); + __pmFreeInResult(res); + return sts; + } + } + if (getall || getname) { + if ((res->namelist = (char **)malloc(res->numinst * sizeof(char *))) == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance namelist", res->numinst * sizeof(char *), PM_RECOV_ERR); + free(res->instlist); + __pmFreeInResult(res); + return sts; + } + } + } + else + return PM_ERR_INDOM; + + if (indom == logindom) { + res->indom = logindom; + + if (getall) { /* get instance ids and names */ + for (i = 0; i < nports; i++) { + res->instlist[i] = ports[i].pid; + res->namelist[i] = strdup(ports[i].name); + if (res->namelist[i] == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance pmGetInDom", + strlen(ports[i].name), PM_RECOV_ERR); + /* ensure pmFreeInResult only gets valid pointers */ + res->numinst = i; + break; + } + } + } + else if (getname) { /* given id, get name */ + for (i = 0; i < nports; i++) { + if (inst == ports[i].pid) + break; + } + if (i == nports) { + sts = PM_ERR_INST; + res->namelist[0] = NULL; + } + else { + res->namelist[0] = strdup(ports[i].name); + if (res->namelist[0] == NULL) { + __pmNoMem("pmcd_instance pmNameInDom", + strlen(ports[i].name), PM_RECOV_ERR); + sts = -oserror(); + } + } + } + else { /* given name, get id */ + for (i = 0; i < nports; i++) { + if (strcmp(name, ports[i].name) == 0) + break; + } + if (i == nports) + sts = PM_ERR_INST; + else + res->instlist[0] = ports[i].pid; + } + } + else if (indom == pmieindom) { + res->indom = pmieindom; + + if (getall) { /* get instance ids and names */ + for (i = 0; i < pmiecount; i++) { + res->instlist[i] = pmies[i].pid; + res->namelist[i] = strdup(pmies[i].name); + if (res->namelist[i] == NULL) { + sts = -oserror(); + __pmNoMem("pmie_instance pmGetInDom", + strlen(pmies[i].name), PM_RECOV_ERR); + /* ensure pmFreeInResult only gets valid pointers */ + res->numinst = i; + break; + } + } + } + else if (getname) { /* given id, get name */ + for (i = 0; i < pmiecount; i++) { + if (inst == pmies[i].pid) + break; + } + if (i == pmiecount) { + sts = PM_ERR_INST; + res->namelist[0] = NULL; + } + else { + res->namelist[0] = strdup(pmies[i].name); + if (res->namelist[0] == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance pmNameInDom", + strlen(pmies[i].name), PM_RECOV_ERR); + } + } + } + else { /* given name, get id */ + for (i = 0; i < pmiecount; i++) { + if (strcmp(name, pmies[i].name) == 0) + break; + } + if (i == pmiecount) + sts = PM_ERR_INST; + else + res->instlist[0] = pmies[i].pid; + } + } + else if (indom == pmdaindom) { + res->indom = pmdaindom; + + if (getall) { /* get instance ids and names */ + for (i = 0; i < nAgents; i++) { + res->instlist[i] = agent[i].pmDomainId; + res->namelist[i] = strdup(agent[i].pmDomainLabel); + if (res->namelist[i] == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance pmGetInDom", + strlen(agent[i].pmDomainLabel), PM_RECOV_ERR); + /* ensure pmFreeInResult only gets valid pointers */ + res->numinst = i; + break; + } + } + } + else if (getname) { /* given id, get name */ + for (i = 0; i < nAgents; i++) { + if (inst == agent[i].pmDomainId) + break; + } + if (i == nAgents) { + sts = PM_ERR_INST; + res->namelist[0] = NULL; + } + else { + res->namelist[0] = strdup(agent[i].pmDomainLabel); + if (res->namelist[0] == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance pmNameInDom", + strlen(agent[i].pmDomainLabel), PM_RECOV_ERR); + } + } + } + else { /* given name, get id */ + for (i = 0; i < nAgents; i++) { + if (strcmp(name, agent[i].pmDomainLabel) == 0) + break; + } + if (i == nAgents) + sts = PM_ERR_INST; + else + res->instlist[0] = agent[i].pmDomainId; + } + } + else if (indom == clientindom) { + res->indom = clientindom; + + if (getall) { /* get instance ids and names */ + int k = 0; + for (i = 0; i < nClients; i++) { + char buf[11]; /* enough for 32-bit client seq number */ + if (!client[i].status.connected) + continue; + res->instlist[k] = client[i].seq; + snprintf(buf, sizeof(buf), "%u", client[i].seq); + res->namelist[k] = strdup(buf); + if (res->namelist[k] == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance pmGetInDom", + strlen(buf), PM_RECOV_ERR); + /* ensure pmFreeInResult only gets valid pointers */ + res->numinst = i; + break; + } + k++; + } + } + else if (getname) { /* given id, get name */ + for (i = 0; i < nClients; i++) { + if (client[i].status.connected && inst == client[i].seq) + break; + } + if (i == nClients) { + sts = PM_ERR_INST; + res->namelist[0] = NULL; + } + else { + char buf[11]; /* enough for 32-bit client seq number */ + snprintf(buf, sizeof(buf), "%u", (unsigned int)inst); + res->namelist[0] = strdup(buf); + if (res->namelist[0] == NULL) { + sts = -oserror(); + __pmNoMem("pmcd_instance pmNameInDom", + strlen(buf), PM_RECOV_ERR); + } + } + } + else { /* given name, get id */ + char buf[11]; /* enough for 32-bit client seq number */ + for (i = 0; i < nClients; i++) { + if (!client[i].status.connected) + continue; + snprintf(buf, sizeof(buf), "%u", client[i].seq); + if (strcmp(name, buf) == 0) + break; + } + if (i == nClients) + sts = PM_ERR_INST; + else + res->instlist[0] = client[i].seq; + } + } + + if (sts < 0) { + __pmFreeInResult(res); + return sts; + } + + *result = res; + return 0; +} + +/* + * numval != 1, so re-do vset[i] allocation + */ +static int +vset_resize(pmResult *rp, int i, int onumval, int numval) +{ + int expect = numval; + + if (rp->vset[i] != NULL) { + free(rp->vset[i]); + } + + if (numval < 0) + expect = 0; + + rp->vset[i] = (pmValueSet *)malloc(sizeof(pmValueSet) + (expect-1)*sizeof(pmValue)); + + if (rp->vset[i] == NULL) { + if (i) { + /* we're doomed ... reclaim pmValues 0, 1, ... i-1 */ + rp->numpmid = i; + __pmFreeResultValues(rp); + } + return -1; + } + + rp->vset[i]->numval = numval; + return 0; +} + +static char * +simabi() +{ +#if defined(__linux__) || defined(IS_GNU) +# if defined(__i386__) + return "ia32"; +# elif defined(__ia64__) || defined(__ia64) + return "ia64"; +# else + return SIM_ABI; /* SIM_ABI is defined in the linux Makefile */ +# endif /* __linux__ */ +#elif defined(IS_SOLARIS) + static char abi[32]; + if (sysinfo(SI_ARCHITECTURE_NATIVE, abi, sizeof(abi)) < 0) { + return "unknown"; + } else { + return abi; + } +#elif defined(IS_FREEBSD) || defined(IS_NETBSD) + return "elf"; +#elif defined(IS_DARWIN) + return "Mach-O " SIM_ABI; +#elif defined(IS_MINGW) + return "x86_64"; +#elif defined(IS_AIX) + return "powerpc"; +#else + !!! bozo : dont know which executable format pmcd should be!!! +#endif +} + +static char * +tzinfo(void) +{ + /* + * __pmTimezone() caches its result in $TZ - pmcd is long running, + * however, and we *really* want to see changes in the timezone or + * daylight savings state via pmcd.timezone, so we clear TZ first. + */ +#ifdef HAVE_UNSETENV + unsetenv("TZ"); +#else /* MINGW */ + putenv("TZ="); +#endif + return __pmTimezone(); +} + +static int +extract_service(const char *path, char *name, pid_t *pid) +{ + int length, sep = __pmPathSeparator(); + char fullpath[MAXPATHLEN]; + char buffer[64]; + FILE *fp; + + /* check basename has a ".pid" suffix */ + if ((length = strlen(name)) < 5) + return 0; + length -= 4; + if (strcmp(&name[length], ".pid") != 0) + return 0; + + /* extract PID lurking within the file */ + snprintf(fullpath, sizeof(fullpath), "%s%c%s", path, sep, name); + if ((fp = fopen(fullpath, "r")) == NULL) + return 0; + sep = fscanf(fp, "%63s", buffer); + fclose(fp); + if (sep != 1) + return 0; + *pid = atoi(buffer); + + /* finally setup service name to return */ + name[length] = '\0'; + return length; +} + +char * +services(void) +{ + static char servicelist[128]; + static struct stat lastsbuf; + pid_t pid; + struct dirent *dp; + struct stat statbuf; + char *path; + DIR *rundir; + int length, offset; + + path = pmGetConfig("PCP_RUN_DIR"); + if (stat(path, &statbuf) == 0) { + if (stat_time_differs(&statbuf, &lastsbuf)) { + lastsbuf = statbuf; + + /* by definition, pmcd is currently running */ + strcpy(servicelist, PM_SERVER_SERVICE_SPEC); + offset = sizeof(PM_SERVER_SERVICE_SPEC) - 1; + + /* iterate through directory, building up services string */ + if ((rundir = opendir(path)) == NULL) { + __pmNotifyErr(LOG_ERR, "pmcd pmda cannot open %s: %s", + path, osstrerror()); + return servicelist; + } + while ((dp = readdir(rundir)) != NULL) { + if (dp->d_name[0] == '.') + continue; + length = sizeof(PM_SERVER_SERVICE_SPEC) - 1; + if (strncmp(dp->d_name, PM_SERVER_SERVICE_SPEC, length) == 0) + continue; + if ((length = extract_service(path, dp->d_name, &pid)) <= 0) + continue; + if (!__pmProcessExists(pid)) + continue; + if (offset + 1 + length + 1 > sizeof(servicelist)) + continue; + servicelist[offset++] = ' '; + strcpy(&servicelist[offset], dp->d_name); + offset += length; + } + closedir(rundir); + } + } else { + strcpy(servicelist, PM_SERVER_SERVICE_SPEC); + } + return servicelist; +} + +static char * +hostnameinfo(void) +{ + static char host[MAXHOSTNAMELEN]; + char *name; + + (void)gethostname(host, MAXHOSTNAMELEN); + name = host; + + return name; +} + +static int +fetch_feature(int item, pmAtomValue *avp) +{ + if (item < 0 || item >= PM_SERVER_FEATURES) + return PM_ERR_PMID; + avp->ul = __pmServerHasFeature((__pmServerFeature)item); + return 0; +} + +static int +fetch_cputime(int item, int ctx, pmAtomValue *avp) +{ + double usr, sys; + double cputime; + + if (item < 0 || item > 1) { + return PM_ERR_PMID; + } + if (ctx < 0) { + /* should not happen */ + return PM_ERR_NOTCONN; + } + __pmProcessRunTimes(&usr, &sys); + cputime = (usr+sys)*1000; + if (ctx >= num_ctx) + grow_ctxtab(ctx); + if (item == 0) { /* pmcd.cputime.total */ + avp->ull = (__uint64_t)cputime; + } + else if (item == 1) { /* pmcd.cputime.per_pdu_in */ + int j; + int pdu_in; + for (pdu_in = j = 0; j <= PDU_MAX; j++) + pdu_in += __pmPDUCntIn[j]; + if (ctxtab[ctx].state == CTX_INACTIVE) { + /* first call for this context */ + ctxtab[ctx].state = CTX_ACTIVE; + avp->d = cputime*1000/pdu_in; + } + else { + if (pdu_in > ctxtab[ctx].last_pdu_in) + avp->d = 1000*(cputime-ctxtab[ctx].last_cputime)/(pdu_in-ctxtab[ctx].last_pdu_in); + else { + /* should not happen, as you need another pdu to get here */ + avp->d = 0; + } + } + ctxtab[ctx].last_cputime = cputime; + ctxtab[ctx].last_pdu_in = pdu_in; + } + return 0; +} + +static void +end_context(int ctx) +{ + if (ctx >= 0 && ctx < num_ctx && ctxtab[ctx].state == CTX_ACTIVE) { + ctxtab[ctx].state = CTX_INACTIVE; + } +} + +static int +pmcd_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; /* over pmidlist[] */ + int j; + int sts, nports; + int need; + int numval; + int valfmt; + unsigned long datasize; + static pmResult *res = NULL; + static int maxnpmids = 0; + char *host = NULL; /* refresh max once per fetch */ + pmiestats_t *pmie; + pmValueSet *vset; + pmDesc *dp = NULL; /* initialize to pander to gcc */ + __pmID_int *pmidp; + pmAtomValue atom; + __pmLogPort *lpp; + + if (numpmid > maxnpmids) { + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = (int)sizeof(pmResult) + (numpmid - 1) * (int)sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == NULL) + return -ENOMEM; + maxnpmids = numpmid; + } + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + for (i = 0; i < numpmid; i++) { + /* Allocate a pmValueSet with room for just one value. Even for the + * pmlogger port metric which has an instance domain, most of the time + * there will only be one logger running (i.e. one instance). For the + * infrequent cases resize the value set later. + */ + res->vset[i] = NULL; + if (vset_resize(res, i, 0, 1) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + vset->vlist[0].inst = PM_IN_NULL; + + for (j = 0; j < ndesc; j++) { + if (desctab[j].pmid == pmidlist[i]) { + dp = &desctab[j]; + break; + } + } + if (j == ndesc) { + /* Error, need a smaller vset */ + if (vset_resize(res, i, 1, PM_ERR_PMID) == -1) + return -ENOMEM; + res->vset[i]->pmid = pmidlist[i]; + continue; + } + + valfmt = -1; + sts = 0; + + pmidp = (__pmID_int *)&pmidlist[i]; + switch (pmidp->cluster) { + + case 0: /* global metrics */ + switch (pmidp->item) { + case 0: /* control.debug */ + atom.l = pmDebug; + break; + case 1: /* datasize */ + __pmProcessDataSize(&datasize); + atom.ul = datasize; + break; + case 2: /* numagents */ + atom.ul = 0; + for (j = 0; j < nAgents; j++) + if (agent[j].status.connected) + atom.ul++; + break; + case 3: /* numclients */ + atom.ul = 0; + for (j = 0; j < nClients; j++) + if (client[j].status.connected) + atom.ul++; + break; + case 4: /* control.timeout */ + atom.ul = _pmcd_timeout; + break; + case 5: /* timezone $TZ */ + atom.cp = tzinfo(); + break; + case 6: /* simabi (pmcd calling convention) */ + atom.cp = simabi(); + break; + case 7: /* version */ + atom.cp = PCP_VERSION; + break; + case 8: /* register */ + for (j = numval = 0; j < NUMREG; j++) { + if (__pmInProfile(regindom, _profile, j)) + numval++; + } + if (numval != 1) { + /* need a different vset size */ + if (vset_resize(res, i, 1, numval) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + } + for (j = numval = 0; j < NUMREG; j++) { + if (!__pmInProfile(regindom, _profile, j)) + continue; + vset->vlist[numval].inst = j; + atom.l = reg[j]; + sts = __pmStuffValue(&atom, &vset->vlist[numval], dp->type); + if (sts < 0) + break; + valfmt = sts; + numval++; + } + break; + case 9: /* traceconn */ + atom.l = (_pmcd_trace_mask & TR_MASK_CONN) ? 1 : 0; + break; + case 10: /* tracepdu */ + atom.l = (_pmcd_trace_mask & TR_MASK_PDU) ? 1 : 0; + break; + case 11: /* tracebufs */ + atom.l = _pmcd_trace_nbufs; + break; + case 12: /* dumptrace ... always 0 */ + atom.l = 0; + break; + case 13: /* dumpconn ... always 0 */ + atom.l = 0; + break; + case 14: /* tracenobuf */ + atom.l = (_pmcd_trace_mask & TR_MASK_NOBUF) ? 1 : 0; + break; + case 15: /* sighup ... always 0 */ + atom.l = 0; + break; + case 16: /* services */ + atom.cp = services(); + break; + case 17: /* openfds */ + atom.ul = (unsigned int)pmcd_hi_openfds; + break; + case 18: /* buf.alloc */ + case 19: /* buf.free */ + for (j = numval = 0; j < nbufsz; j++) { + if (__pmInProfile(bufindom, _profile, bufinst[j].inst)) + numval++; + } + if (numval != 1) { + /* need a different vset size */ + if (vset_resize(res, i, 1, numval) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + } + for (j = numval = 0; j < nbufsz; j++) { + int alloced; + int free; + int xtra_alloced; + int xtra_free; + if (!__pmInProfile(bufindom, _profile, bufinst[j].inst)) + continue; + vset->vlist[numval].inst = bufinst[j].inst; + /* PDUBuf pool */ + __pmCountPDUBuf(bufinst[j].inst, &alloced, &free); + /* + * the 2K buffer count also includes + * the 3K, 4K, ... buffers, so sub + * these ... which are reported as + * the 3K buffer count + */ + __pmCountPDUBuf(bufinst[j].inst + 1024, &xtra_alloced, &xtra_free); + alloced -= xtra_alloced; + free -= xtra_free; + if (pmidp->item == 18) + atom.l = alloced; + else + atom.l = free; + sts = __pmStuffValue(&atom, &vset->vlist[numval], dp->type); + if (sts < 0) + break; + valfmt = sts; + numval++; + } + break; + + case 20: /* build */ + atom.cp = BUILD; + break; + + case 21: /* hostname */ + if (_pmcd_hostname) { + atom.cp = _pmcd_hostname; + } else { + if (!host) + host = hostnameinfo(); + atom.cp = host; + } + break; + default: + sts = atom.l = PM_ERR_PMID; + break; + } + break; + + case 1: /* PDUs received */ + if (pmidp->item == _TOTAL) { + /* total */ + atom.ul = 0; + for (j = 0; j <= PDU_MAX; j++) + atom.ul += __pmPDUCntIn[j]; + } + else if (pmidp->item > PDU_MAX+1) + sts = atom.l = PM_ERR_PMID; + else if (pmidp->item < _TOTAL) + atom.ul = __pmPDUCntIn[pmidp->item]; + else + atom.ul = __pmPDUCntIn[pmidp->item-1]; + break; + + case 2: /* PDUs sent */ + if (pmidp->item == _TOTAL) { + /* total */ + atom.ul = 0; + for (j = 0; j <= PDU_MAX; j++) + atom.ul += __pmPDUCntOut[j]; + } + else if (pmidp->item > PDU_MAX+1) + sts = atom.l = PM_ERR_PMID; + else if (pmidp->item < _TOTAL) + atom.ul = __pmPDUCntOut[pmidp->item]; + else + atom.ul = __pmPDUCntOut[pmidp->item-1]; + break; + + case 3: /* pmlogger control port, pmcd_host, archive and host */ + /* find all ports. localhost => no recursive pmcd access */ + nports = __pmLogFindPort("localhost", PM_LOG_ALL_PIDS, &lpp); + if (nports < 0) { + sts = nports; + break; + } + for (j = numval = 0; j < nports; j++) { + if (__pmInProfile(logindom, _profile, lpp[j].pid)) + numval++; + } + if (numval != 1) { + /* need a different vset size */ + if (vset_resize(res, i, 1, numval) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + } + for (j = numval = 0; j < nports; j++) { + if (!__pmInProfile(logindom, _profile, lpp[j].pid)) + continue; + vset->vlist[numval].inst = lpp[j].pid; + switch (pmidp->item) { + case 0: /* pmlogger.port */ + atom.ul = lpp[j].port; + break; + case 1: /* pmlogger.pmcd_host */ + atom.cp = lpp[j].pmcd_host ? + lpp[j].pmcd_host : ""; + break; + case 2: /* pmlogger.archive */ + atom.cp = lpp[j].archive ? lpp[j].archive : ""; + break; + case 3: /* pmlogger.host */ + if (!host) + host = hostnameinfo(); + atom.cp = host; + break; + default: + sts = atom.l = PM_ERR_PMID; + break; + } + if (sts >= 0) + sts = __pmStuffValue(&atom, &vset->vlist[numval], dp->type); + if (sts < 0) + break; + valfmt = sts; + numval++; + } + break; + + case 4: /* PMDA metrics */ + for (j = numval = 0; j < nAgents; j++) { + if (__pmInProfile(pmdaindom, _profile, agent[j].pmDomainId)) + numval++; + } + if (numval != 1) { + /* need a different vset size */ + if (vset_resize(res, i, 1, numval) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + } + for (j = numval = 0; j < nAgents; j++) { + if (!__pmInProfile(pmdaindom, _profile, agent[j].pmDomainId)) + continue; + vset->vlist[numval].inst = agent[j].pmDomainId; + switch (pmidp->item) { + case 0: /* agent.type */ + atom.ul = agent[j].ipcType << 1; + break; + case 1: /* agent.status */ + if (agent[j].status.notReady) + atom.l = 1; + else if (agent[j].status.connected) + atom.l = 0; + else + atom.l = agent[j].reason; + break; + default: + sts = atom.l = PM_ERR_PMID; + break; + } + if (sts >= 0) + sts = __pmStuffValue(&atom, &vset->vlist[numval], dp->type); + if (sts < 0) + break; + valfmt = sts; + numval++; + } + if (numval > 0) { + pmResult sortme; + sortme.numpmid = 1; + sortme.vset[0] = vset; + pmSortInstances(&sortme); + } + break; + + + case 5: /* pmie metrics */ + refresh_pmie_indom(); + for (j = numval = 0; j < npmies; j++) { + if (__pmInProfile(pmieindom, _profile, pmies[j].pid)) + numval++; + } + if (numval != 1) { + /* need a different vset size */ + if (vset_resize(res, i, 1, numval) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + } + for (j = numval = 0; j < npmies; ++j) { + if (!__pmInProfile(pmieindom, _profile, pmies[j].pid)) + continue; + vset->vlist[numval].inst = pmies[j].pid; + pmie = (pmiestats_t *)pmies[j].mmap; + switch (pmidp->item) { + case 0: /* pmie.configfile */ + atom.cp = pmie->config; + break; + case 1: /* pmie.logfile */ + atom.cp = pmie->logfile; + break; + case 2: /* pmie.pmcd_host */ + atom.cp = pmie->defaultfqdn; + break; + case 3: /* pmie.numrules */ + atom.ul = pmie->numrules; + break; + case 4: /* pmie.actions */ + atom.ul = pmie->actions; + break; + case 5: /* pmie.eval.true */ + atom.ul = pmie->eval_true; + break; + case 6: /* pmie.eval.false */ + atom.ul = pmie->eval_false; + break; + case 7: /* pmie.eval.unknown */ + atom.ul = pmie->eval_unknown; + break; + case 8: /* pmie.eval.expected */ + atom.f = pmie->eval_expected; + break; + case 9: /* pmie.eval.actual */ + atom.ul = pmie->eval_actual; + break; + default: + sts = atom.l = PM_ERR_PMID; + break; + } + if (sts >= 0) + sts = __pmStuffValue(&atom, &vset->vlist[numval], dp->type); + if (sts < 0) + break; + valfmt = sts; + numval++; + } + if (numval > 0) { + pmResult sortme; + sortme.numpmid = 1; + sortme.vset[0] = vset; + pmSortInstances(&sortme); + } + break; + + case 6: /* client metrics */ + for (j = numval = 0; j < nClients; j++) { + if (!client[j].status.connected) + continue; + if (__pmInProfile(clientindom, _profile, client[j].seq)) + numval++; + } + if (numval != 1) { + /* need a different vset size */ + if (vset_resize(res, i, 1, numval) == -1) + return -ENOMEM; + vset = res->vset[i]; + vset->pmid = pmidlist[i]; + } + for (j = numval = 0; j < nClients; ++j) { + int k; + char ctim[sizeof("Thu Nov 24 18:22:48 1986\n")]; + if (!client[j].status.connected) + continue; + if (!__pmInProfile(clientindom, _profile, client[j].seq)) + continue; + vset->vlist[numval].inst = client[j].seq; + switch (pmidp->item) { + case 0: /* client.whoami */ + for (k = 0; k < nwhoamis; k++) { + if (whoamis[k].seq == client[j].seq) { + atom.cp = whoamis[k].value; + break; + } + } + if (k == nwhoamis) + /* no id registered, so no value */ + atom.cp = ""; + break; + case 1: /* client.start_date */ + atom.cp = strcpy(ctim, ctime(&client[j].start)); + /* trim trailing \n */ + k = strlen(atom.cp); + atom.cp[k-1] = '\0'; + break; + default: + sts = atom.l = PM_ERR_PMID; + break; + } + if (sts >= 0) + sts = __pmStuffValue(&atom, &vset->vlist[numval], dp->type); + if (sts < 0) + break; + valfmt = sts; + numval++; + } + if (numval > 0) { + pmResult sortme; + sortme.numpmid = 1; + sortme.vset[0] = vset; + pmSortInstances(&sortme); + } + break; + + case 7: /* cputime metrics */ + sts = fetch_cputime(pmidp->item, pmda->e_context, &atom); + break; + + case 8: /* feature metrics */ + sts = fetch_feature(pmidp->item, &atom); + break; + } + + if (sts == 0 && valfmt == -1 && vset->numval == 1) + sts = valfmt = __pmStuffValue(&atom, &vset->vlist[0], dp->type); + + if (sts < 0) { + /* failure, encode status in numval, need a different vset size */ + if (vset_resize(res, i, vset->numval, sts) == -1) + return -ENOMEM; + } + else + vset->valfmt = valfmt; + } + *resp = res; + + return 0; +} + +static int +pmcd_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda) +{ + int i; + + for (i = 0; i < ndesc; i++) { + if (desctab[i].pmid == pmid) { + *desc = desctab[i]; + return 0; + } + } + return PM_ERR_PMID; +} + +static int +pmcd_store(pmResult *result, pmdaExt *pmda) +{ + int i; + pmValueSet *vsp; + int sts = 0; + __pmID_int *pmidp; + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + if (pmidp->cluster == 0) { + if (pmidp->item == 0) { /* pmcd.control.debug */ + pmDebug = vsp->vlist[0].value.lval; + } + else if (pmidp->item == 4) { /* pmcd.control.timeout */ + int val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + break; + } + if (val != _pmcd_timeout) { + _pmcd_timeout = val; + } + } + else if (pmidp->item == 8) { /* pmcd.control.register */ + int j; + for (j = 0; j < vsp->numval; j++) { + if (0 <= vsp->vlist[j].inst && vsp->vlist[j].inst < NUMREG) + reg[vsp->vlist[j].inst] = vsp->vlist[j].value.lval; + else { + sts = PM_ERR_INST; + break; + } + } + } + else if (pmidp->item == 9) { /* pmcd.control.traceconn */ + int val = vsp->vlist[0].value.lval; + if (val == 0) + _pmcd_trace_mask &= (~TR_MASK_CONN); + else if (val == 1) + _pmcd_trace_mask |= TR_MASK_CONN; + else { + sts = PM_ERR_CONV; + break; + } + } + else if (pmidp->item == 10) { /* pmcd.control.tracepdu */ + int val = vsp->vlist[0].value.lval; + if (val == 0) + _pmcd_trace_mask &= (~TR_MASK_PDU); + else if (val == 1) + _pmcd_trace_mask |= TR_MASK_PDU; + else { + sts = PM_ERR_CONV; + break; + } + } + else if (pmidp->item == 11) { /* pmcd.control.tracebufs */ + int val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + break; + } + pmcd_init_trace(val); + } + else if (pmidp->item == 12) { /* pmcd.control.dumptrace */ + pmcd_dump_trace(stderr); + } + else if (pmidp->item == 13) { /* pmcd.control.dumpconn */ + time_t now; + time(&now); + fprintf(stderr, "\n->Current PMCD clients at %s", ctime(&now)); + ShowClients(stderr); + } + else if (pmidp->item == 14) { /* pmcd.control.tracenobuf */ + int val = vsp->vlist[0].value.lval; + if (val == 0) + _pmcd_trace_mask &= (~TR_MASK_NOBUF); + else if (val == 1) + _pmcd_trace_mask |= TR_MASK_NOBUF; + else { + sts = PM_ERR_CONV; + break; + } + } + else if (pmidp->item == 15) { /* pmcd.control.sighup */ +#ifdef HAVE_SIGHUP + /* + * send myself SIGHUP + */ + __pmNotifyErr(LOG_INFO, "pmcd reset via pmcd.control.sighup"); + raise(SIGHUP); +#endif + } + else { + sts = PM_ERR_PMID; + break; + } + } + else if (pmidp->cluster == 6) { + if (pmidp->item == 0) { /* pmcd.client.whoami */ + /* + * Expect one value for one instance (PM_IN_NULL) + * + * Use the value from the pmResult to change the value + * for the client[] that matches the current pmcd client. + */ + char *cp = vsp->vlist[0].value.pval->vbuf; + int j; + int last_free = -1; + + if (vsp->numval != 1 || vsp->vlist[0].inst != PM_IN_NULL) { + return PM_ERR_INST; + } + for (j = 0; j < nwhoamis; j++) { + if (whoamis[j].id == -1) { + /* slot in whoamis[] not in use */ + last_free = j; + continue; + } + if (whoamis[j].id == this_client_id && + whoamis[j].seq == client[this_client_id].seq) { + /* found the one to replace */ + free(whoamis[j].value); + break; + } + if (!client[whoamis[j].id].status.connected || + client[whoamis[j].id].seq != whoamis[j].seq) { + /* old whoamis[] entry, mark as available for reuse */ + free(whoamis[j].value); + whoamis[j].id = -1; + last_free = j; + } + } + if (j == nwhoamis) { + if (last_free != -1) { + j = last_free; + } + else { + nwhoamis++; + if ((whoamis = (whoami_t *)realloc(whoamis, nwhoamis*sizeof(whoamis[0]))) == NULL) { + __pmNoMem("pmstore whoami", nwhoamis*sizeof(whoamis[0]), PM_RECOV_ERR); + nwhoamis = 0; + return -ENOMEM; + } + } + whoamis[j].id = this_client_id; + whoamis[j].seq = client[this_client_id].seq; + } + whoamis[j].value = strdup(cp); + } + else { + sts = PM_ERR_PMID; + break; + } + } + else { + /* not one of the metrics we are willing to change */ + sts = PM_ERR_PMID; + break; + } + } + + return sts; +} + +void +__PMDA_INIT_CALL +pmcd_init(pmdaInterface *dp) +{ + char helppath[MAXPATHLEN]; + int sep = __pmPathSeparator(); + + snprintf(helppath, sizeof(helppath), "%s%c" "pmcd" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_5, "pmcd", helppath); + + dp->version.four.profile = pmcd_profile; + dp->version.four.fetch = pmcd_fetch; + dp->version.four.desc = pmcd_desc; + dp->version.four.instance = pmcd_instance; + dp->version.four.store = pmcd_store; + dp->version.four.ext->e_endCallBack = end_context; + + init_tables(dp->domain); + + pmdaInit(dp, NULL, 0, NULL, 0); +} diff --git a/src/pmdas/postfix/GNUmakefile b/src/pmdas/postfix/GNUmakefile new file mode 100644 index 0000000..a38b0c3 --- /dev/null +++ b/src/pmdas/postfix/GNUmakefile @@ -0,0 +1,48 @@ +#!gmake +# +# Copyright (c) 2009 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = postfix +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/postfix/Install b/src/pmdas/postfix/Install new file mode 100644 index 0000000..07fe957 --- /dev/null +++ b/src/pmdas/postfix/Install @@ -0,0 +1,34 @@ +#! /bin/sh +# +# Copyright (c) 2009 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the postfix PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=postfix +perl_opt=true +daemon_opt=false +forced_restart=false + +which qshape >/dev/null 2>&1 +if test $? -ne 0; then + echo "Postfix qshape utility is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/postfix/Remove b/src/pmdas/postfix/Remove new file mode 100644 index 0000000..5d06c62 --- /dev/null +++ b/src/pmdas/postfix/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2009 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the postfix PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=postfix + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/postfix/pmdapostfix.pl b/src/pmdas/postfix/pmdapostfix.pl new file mode 100644 index 0000000..4acb985 --- /dev/null +++ b/src/pmdas/postfix/pmdapostfix.pl @@ -0,0 +1,266 @@ +# +# Copyright (c) 2012,2014 Red Hat. +# Copyright (c) 2009-2010 Josef 'Jeff' Sipek +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use Time::HiRes qw ( time ); + +use vars qw( $pmda ); +use vars qw( %caches ); +use vars qw( %logstats ); +my @logfiles = ( '/var/log/mail.log', '/var/log/maillog' ); +my $logfile; +my $qshape = 'qshape -b 10 -t 5'; +my $refresh = 5.0; # 5 seconds between qshape refreshes + +my $cached = 0; + +my $postfix_queues_indom = 0; +my @postfix_queues_dom = ( + 0 => 'total', + 1 => '0-5 mins', + 2 => '5-10 mins', + 3 => '10-20 mins', + 4 => '20-40 mins', + 5 => '40-80 mins', + 6 => '80-160 mins', + 7 => '160-320 mins', + 8 => '320-640 mins', + 9 => '640-1280 mins', + 10=> '1280+ mins', + ); + +my $postfix_sent_indom = 1; +my @postfix_sent_dom = ( + 0 => 'smtp', + 1 => 'local', + ); + +my $postfix_received_indom = 2; +my @postfix_received_dom = ( + 0 => 'local', + 1 => 'smtp', + ); + +sub postfix_do_refresh +{ + QUEUE: + foreach my $qname ("maildrop", "incoming", "hold", "active", "deferred") { + unless (open(PIPE, "$qshape $qname |")) { + $pmda->log("couldn't execute '$qshape $qname'"); + next QUEUE; + } + while() { + last if (/^[\t ]*TOTAL /); + } + close PIPE; + + unless (/^[\t ]*TOTAL /) { + $pmda->log("malformed output for '$qshape $qname': $_"); + next QUEUE; + } + + s/^[\t ]*//; + s/[\t ]+/ /g; + + my @items = split(/ /); + + $caches{$qname}{0} = $items[1]; + $caches{$qname}{1} = $items[2]; + $caches{$qname}{2} = $items[3]; + $caches{$qname}{3} = $items[4]; + $caches{$qname}{4} = $items[5]; + $caches{$qname}{5} = $items[6]; + $caches{$qname}{6} = $items[7]; + $caches{$qname}{7} = $items[8]; + $caches{$qname}{8} = $items[9]; + $caches{$qname}{9} = $items[10]; + $caches{$qname}{10} = $items[11]; + } +} + +sub postfix_log_parser +{ + ( undef, $_ ) = @_; + + if (/status=sent/) { + return unless (/ postfix\//); + + my $relay = ""; + + if (/relay=([^,]+)/o) { + $relay = $1; + } + + if ($relay !~ /\[/o) { + # if we are about to define a new instance, let's add it to the + # domain as well + my $idx = 0; + my $key; + my %tmp = @postfix_sent_dom; + + foreach $key (sort keys %tmp) { + last if ($relay eq $tmp{$key}); + $idx += 1; + } + + if ((2 * $idx) == @postfix_sent_dom) { + push(@postfix_sent_dom, $idx=>$relay); + $pmda->replace_indom($postfix_sent_indom, \@postfix_sent_dom); + } + + $logstats{"sent"}{$idx} += 1; + } else { + $logstats{"sent"}{0} += 1; + } + } elsif (/smtpd.*client=/) { + $logstats{"received"}{1} += 1; + } elsif (/pickup.*(sender|uid)=/) { + $logstats{"received"}{0} += 1; + } +} + +sub postfix_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + + my $now = time; + + #$pmda->log("postfix_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + if (!defined($metric_name)) { return (PM_ERR_PMID, 0); } + + if ($cluster == 0) { + my $qname; + + if ($now - $cached > $refresh) { + postfix_do_refresh(); + $cached = $now; + } + + $qname = $metric_name; + $qname =~ s/^postfix\.queues\.//; + + return (PM_ERR_AGAIN, 0) unless defined($caches{$qname}); + return ($caches{$qname}{$inst}, 1); + } elsif ($cluster == 1) { + my $dir = $metric_name; + $dir =~ s/^postfix\.//; + + return (PM_ERR_AGAIN, 0) unless defined($logstats{$dir}); + return (PM_ERR_AGAIN, 0) unless defined($logstats{$dir}{$inst}); + return ($logstats{$dir}{$inst}, 1); + } + + return (PM_ERR_PMID, 0); +} + +$pmda = PCP::PMDA->new('postfix', 103); +$pmda->connect_pmcd; + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, $postfix_queues_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.queues.maildrop', '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, $postfix_queues_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.queues.incoming', '', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, $postfix_queues_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.queues.hold', '', ''); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U32, $postfix_queues_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.queues.active', '', ''); +$pmda->add_metric(pmda_pmid(0,4), PM_TYPE_U32, $postfix_queues_indom, + PM_SEM_INSTANT, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.queues.deferred', '', ''); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U32, $postfix_sent_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.sent', '', ''); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U32, $postfix_received_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postfix.received', '', ''); + +$logstats{"sent"}{0} = 0; +$logstats{"received"}{0} = 0; +$logstats{"received"}{1} = 0; + +foreach my $file ( @logfiles ) { + if ( -r $file ) { + $logfile = $file; + } +} +die 'No Postfix log file found' unless defined($logfile); + +$pmda->add_indom($postfix_queues_indom, \@postfix_queues_dom, '', ''); +$pmda->add_indom($postfix_sent_indom, \@postfix_sent_dom, '', ''); +$pmda->add_indom($postfix_received_indom, \@postfix_received_dom, '', ''); +$pmda->add_tail($logfile, \&postfix_log_parser, 0); +$pmda->set_fetch_callback(\&postfix_fetch_callback); +$pmda->set_user('postfix'); +$pmda->run; + +=pod + +=head1 NAME + +pmdapostfix - Postfix performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +mail queue sizes as reported by qshape(1), as well as aggregate statistics +collected from mail.log. + +=head1 INSTALLATION + +If you want access to the names and values for the Postfix performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/postfix + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/postfix + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/postfix/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/postfix/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/postfix.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and qshape(1). diff --git a/src/pmdas/postgresql/GNUmakefile b/src/pmdas/postgresql/GNUmakefile new file mode 100644 index 0000000..114c0cf --- /dev/null +++ b/src/pmdas/postgresql/GNUmakefile @@ -0,0 +1,51 @@ +#!gmake +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = postgresql +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl pmlogconf.summary + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/postgresql/Install b/src/pmdas/postgresql/Install new file mode 100755 index 0000000..3cf31be --- /dev/null +++ b/src/pmdas/postgresql/Install @@ -0,0 +1,40 @@ +#! /bin/sh +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the PostgreSQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=postgresql +perl_opt=true +daemon_opt=false +forced_restart=true + +perl -e "use DBI" 2>/dev/null +if test $? -ne 0; then + echo "Perl database interface (DBI) is not installed" + exit 1 +fi + +perl -e "use DBD::Pg" 2>/dev/null +if test $? -ne 0; then + echo "Postgres database driver (DBD::Pg) is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/postgresql/Remove b/src/pmdas/postgresql/Remove new file mode 100755 index 0000000..db3f251 --- /dev/null +++ b/src/pmdas/postgresql/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the PostgreSQL PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=postgresql + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/postgresql/pmdapostgresql.pl b/src/pmdas/postgresql/pmdapostgresql.pl new file mode 100644 index 0000000..9290981 --- /dev/null +++ b/src/pmdas/postgresql/pmdapostgresql.pl @@ -0,0 +1,1546 @@ +# +# Copyright (c) 2011 Nathan Scott. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use DBI; + +my $database = 'dbi:Pg:dbname=postgres'; +my $username = 'postgres'; +my $password = ''; # DBI parameter, typically unused for postgres + +# Configuration files for overriding the above settings +for my $file ( '/etc/pcpdbi.conf', # system defaults (lowest priority) + pmda_config('PCP_PMDAS_DIR') . '/postgresql/postgresql.conf', + './postgresql.conf' ) { # current directory (high priority) + eval `cat $file` unless ! -f $file; +} + +use vars qw( $pmda $dbh ); +my $pm_in_null = PM_IN_NULL; +my $all_rel_indom = 0; my @all_rel_instances; +my $sys_rel_indom = 1; my @sys_rel_instances; +my $user_rel_indom = 2; my @user_rel_instances; +my $process_indom = 3; my @process_instances; +my $function_indom = 4; my @function_instances; +my $database_indom = 5; my @database_instances; +my $all_index_indom = 6; my @all_index_instances; +my $sys_index_indom = 7; my @sys_index_instances; +my $user_index_indom = 8; my @user_index_instances; +my $all_seq_indom = 9; my @all_seq_instances; +my $sys_seq_indom = 10; my @sys_seq_instances; +my $user_seq_indom = 11; my @user_seq_instances; +my $replicant_indom = 12; my @replicant_instances; + +# hash of hashes holding DB handle, last values, and min version indexed by table name +my %tables_by_name = ( + pg_stat_activity => { handle => undef, values => {}, version => undef }, + pg_stat_bgwriter => { handle => undef, values => {}, version => undef }, + pg_stat_database => { handle => undef, values => {}, version => undef }, + pg_stat_database_conflicts => { handle => undef, values => {}, version => 9.1 }, + pg_stat_replication => { handle => undef, values => {}, version => 9.1 }, + pg_stat_all_tables => { handle => undef, values => {}, version => undef }, + pg_stat_sys_tables => { handle => undef, values => {}, version => undef }, + pg_stat_user_tables => { handle => undef, values => {}, version => undef }, + pg_stat_all_indexes => { handle => undef, values => {}, version => undef }, + pg_stat_sys_indexes => { handle => undef, values => {}, version => undef }, + pg_stat_user_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_all_tables => { handle => undef, values => {}, version => undef }, + pg_statio_sys_tables => { handle => undef, values => {}, version => undef }, + pg_statio_user_tables => { handle => undef, values => {}, version => undef }, + pg_statio_all_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_sys_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_user_indexes => { handle => undef, values => {}, version => undef }, + pg_statio_all_sequences => { handle => undef, values => {}, version => undef }, + pg_statio_sys_sequences => { handle => undef, values => {}, version => undef }, + pg_statio_user_sequences => { handle => undef, values => {}, version => undef }, + pg_stat_user_functions => { handle => undef, values => {}, version => undef }, + pg_stat_xact_user_functions => { handle => undef, values => {}, version => 9.1 }, + pg_stat_xact_all_tables => { handle => undef, values => {}, version => 9.1 }, + pg_stat_xact_sys_tables => { handle => undef, values => {}, version => 9.1 }, + pg_stat_xact_user_tables => { handle => undef, values => {}, version => 9.1 }, + pg_active => { handle => undef, values => {}, version => undef, sql => 'select pg_is_in_recovery(), pg_current_xlog_location()' }, + pg_recovery => { handle => undef, values => {}, version => undef, sql => 'select pg_is_in_recovery(), pg_last_xlog_receive_location(), pg_last_xlog_replay_location()' }, +); + +# hash of hashes holding setup and refresh function, indexed by PMID cluster +my %tables_by_cluster = ( + '0' => { + name => 'pg_stat_activity', + setup => \&setup_activity, + indom => $process_indom, + refresh => \&refresh_activity }, + '1' => { + name => 'pg_stat_bgwriter', + setup => \&setup_bgwriter, + indom => PM_INDOM_NULL, + refresh => \&refresh_bgwriter }, + '2' => { + name => 'pg_stat_database', + setup => \&setup_database, + indom => $database_indom, + refresh => \&refresh_database }, + '3' => { + name => 'pg_stat_user_functions', + setup => \&setup_user_functions, + indom => $function_indom, + refresh => \&refresh_user_functions }, + '4' => { + name => 'pg_stat_xact_user_functions', + setup => \&setup_xact_user_functions, + indom => $function_indom, + refresh => \&refresh_user_functions }, # identical refresh routine + '5' => { + name => 'pg_stat_database_conflicts', + setup => \&setup_database_conflicts, + indom => $database_indom, + refresh => \&refresh_database }, # identical refresh routine + '6' => { + name => 'pg_stat_replication', + setup => \&setup_replication, + indom => $replicant_indom, + refresh => \&refresh_replication }, + '7' => { + name => 'pg_active', + setup => \&setup_active_functions, + indom => PM_INDOM_NULL, + refresh => \&refresh_active_functions }, + '8' => { + name => 'pg_recovery', + setup => \&setup_recovery_functions, + indom => PM_INDOM_NULL, + refresh => \&refresh_recovery_functions }, + '10' => { + name => 'pg_stat_all_tables', + setup => \&setup_stat_tables, + indom => $all_rel_indom, + params => 'all_tables', + refresh => \&refresh_all_tables }, + '11' => { + name => 'pg_stat_sys_tables', + setup => \&setup_stat_tables, + indom => $sys_rel_indom, + params => 'sys_tables', + refresh => \&refresh_sys_tables }, + '12' => { + name => 'pg_stat_user_tables', + setup => \&setup_stat_tables, + indom => $user_rel_indom, + params => 'user_tables', + refresh => \&refresh_user_tables }, + '13' => { + name => 'pg_stat_all_indexes', + setup => \&setup_stat_indexes, + indom => $all_index_indom, + params => 'all_indexes', + refresh => \&refresh_all_indexes }, + '14' => { + name => 'pg_stat_sys_indexes', + setup => \&setup_stat_indexes, + params => 'sys_indexes', + indom => $sys_index_indom, + refresh => \&refresh_sys_indexes }, + '15' => { + name => 'pg_stat_user_indexes', + setup => \&setup_stat_indexes, + indom => $user_index_indom, + params => 'user_indexes', + refresh => \&refresh_user_indexes }, + '16' => { + name => 'pg_stat_xact_all_tables', + setup => \&setup_stat_xact_tables, + indom => $all_rel_indom, + params => 'all_tables', + refresh => \&refresh_xact_all_tables }, + '17' => { + name => 'pg_stat_xact_sys_tables', + setup => \&setup_stat_xact_tables, + indom => $sys_rel_indom, + params => 'sys_tables', + refresh => \&refresh_xact_sys_tables }, + '18' => { + name => 'pg_stat_xact_user_tables', + setup => \&setup_stat_xact_tables, + indom => $user_rel_indom, + params => 'user_tables', + refresh => \&refresh_xact_user_tables }, + '30' => { + name => 'pg_statio_all_tables', + setup => \&setup_statio_tables, + indom => $all_rel_indom, + params => 'all_tables', + refresh => \&refresh_io_all_tables }, + '31' => { + name => 'pg_statio_sys_tables', + setup => \&setup_statio_tables, + indom => $sys_rel_indom, + params => 'sys_tables', + refresh => \&refresh_io_sys_tables }, + '32' => { + name => 'pg_statio_user_tables', + setup => \&setup_statio_tables, + indom => $user_rel_indom, + params => 'user_tables', + refresh => \&refresh_io_user_tables }, + '33' => { + name => 'pg_statio_all_indexes', + setup => \&setup_statio_indexes, + indom => $all_index_indom, + params => 'all_indexes', + refresh => \&refresh_io_all_indexes }, + '34' => { + name => 'pg_statio_sys_indexes', + setup => \&setup_statio_indexes, + indom => $sys_index_indom, + params => 'sys_indexes', + refresh => \&refresh_io_all_indexes }, + '35' => { + name => 'pg_statio_user_indexes', + setup => \&setup_statio_indexes, + indom => $user_index_indom, + params => 'user_indexes', + refresh => \&refresh_io_all_indexes }, + '36' => { + name => 'pg_statio_all_sequences', + setup => \&setup_statio_sequences, + indom => $all_seq_indom, + params => 'all_sequences', + refresh => \&refresh_io_all_sequences }, + '37' => { + name => 'pg_statio_sys_sequences', + setup => \&setup_statio_sequences, + indom => $sys_seq_indom, + params => 'sys_sequences', + refresh => \&refresh_io_sys_sequences }, + '38' => { + name => 'pg_statio_user_sequences', + setup => \&setup_statio_sequences, + params => 'user_sequences', + indom => $user_seq_indom, + refresh => \&refresh_io_user_sequences }, +); + +sub postgresql_version_query +{ + my $handle = $dbh->prepare("select VERSION()"); + + if (defined($handle->execute())) { + my $result = $handle->fetchall_arrayref(); + return 0 unless defined($result); + my $version = $result->[0][0]; + $version =~ s/^PostgreSQL (\d+\.\d+)\.\d+ .*/$1/g; + return $version; + } + return 0; +} + +sub postgresql_connection_setup +{ + if (!defined($dbh)) { + $dbh = DBI->connect($database, $username, $password, + {AutoCommit => 1, pg_bool_tf => 0}); + if (defined($dbh)) { + $pmda->log("PostgreSQL connection established"); + my $version = postgresql_version_query(); + + foreach my $key (keys %tables_by_name) { + my $minversion = $tables_by_name{$key}{version}; + my $sqlquery = $tables_by_name{$key}{sql}; + my $query; + + $minversion = 0 unless defined($minversion); + if ($minversion > $version) { + $pmda->log("Skipping table $key, not supported on $version"); + } elsif (defined($sqlquery)) { + $query = $dbh->prepare($sqlquery); + } else { + $query = $dbh->prepare("select * from $key"); + } + $tables_by_name{$key}{handle} = $query unless !defined($query); + } + } + } +} + +sub postgresql_indoms_setup +{ + $all_rel_indom = $pmda->add_indom($all_rel_indom, \@all_rel_instances, + 'Instance domain for PostgreSQL relations, all tables', ''); + $sys_rel_indom = $pmda->add_indom($sys_rel_indom, \@sys_rel_instances, + 'Instance domain for PostgreSQL relations, system tables', ''); + $user_rel_indom = $pmda->add_indom($user_rel_indom, \@user_rel_instances, + 'Instance domain for PostgreSQL relations, user tables', ''); + + $function_indom = $pmda->add_indom($function_indom, \@function_instances, + 'Instance domain exporting PostgreSQL user functions', ''); + + $process_indom = $pmda->add_indom($process_indom, \@process_instances, + 'Instance domain exporting each PostgreSQL client process', ''); + + $database_indom = $pmda->add_indom($database_indom, \@database_instances, + 'Instance domain exporting each PostgreSQL database', ''); + + $replicant_indom = $pmda->add_indom($replicant_indom, \@replicant_instances, + 'Instance domain exporting PostgreSQL replication processes', ''); + + $all_index_indom = $pmda->add_indom($all_index_indom, \@all_index_instances, + 'Instance domain for PostgreSQL indexes, all tables', ''); + $sys_index_indom = $pmda->add_indom($sys_index_indom, \@sys_index_instances, + 'Instance domain for PostgreSQL indexes, system tables', ''); + $user_index_indom = $pmda->add_indom($user_index_indom, \@user_index_instances, + 'Instance domain for PostgreSQL indexes, user tables', ''); + + $all_seq_indom = $pmda->add_indom($all_seq_indom, \@all_seq_instances, + 'Instance domain for PostgreSQL sequences, all tables', ''); + $sys_seq_indom = $pmda->add_indom($sys_seq_indom, \@sys_seq_instances, + 'Instance domain for PostgreSQL sequences, system tables', ''); + $user_seq_indom = $pmda->add_indom($user_seq_indom, \@user_seq_instances, + 'Instance domain for PostgreSQL sequences, user tables', ''); +} + +sub postgresql_metrics_setup +{ + foreach my $cluster (sort (keys %tables_by_cluster)) { + my $setup = $tables_by_cluster{"$cluster"}{setup}; + my $indom = $tables_by_cluster{"$cluster"}{indom}; + &$setup($cluster, $indom, $tables_by_cluster{"$cluster"}{params}); + } +} + +sub postgresql_refresh +{ + my ($cluster) = @_; + + my $name = $tables_by_cluster{"$cluster"}{name}; + my $refresh = $tables_by_cluster{"$cluster"}{refresh}; + &$refresh($tables_by_name{$name}); +} + +sub postgresql_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $metric_name = pmda_pmid_name($cluster, $item); + my ($key, $value, $valueref, @columns ); + + return (PM_ERR_PMID, 0) unless defined($metric_name); + $key = $metric_name; + $key =~ s/^postgresql\./pg_/; + $key =~ s/\.[a-zA-Z0-9_]*$//; + $key =~ s/\./_/g; + + # $pmda->log("fetch_cb $metric_name $cluster:$item ($inst) - $key"); + + $valueref = $tables_by_name{$key}{values}{"$inst"}; + if (!defined($valueref)) { + return (PM_ERR_INST, 0) unless ($inst == PM_IN_NULL); + return (PM_ERR_VALUE, 0); + } + + @columns = @$valueref; + $value = $columns[$item]; + return (PM_ERR_APPVERSION, 0) unless defined($value); + + return ($value, 1); +} + +# +# Refresh routines - one per table (cluster) - format database query +# result set for later use by the generic fetch callback routine. +# +sub refresh_results +{ + my $tableref = shift; + my %table = %$tableref; + my ( $handle, $valuesref ) = ( $table{handle}, $table{values} ); + my %values = %$valuesref; + + %values = (); # clear any previous values + + if (defined($dbh) && defined($handle)) { + if (defined($handle->execute())) { + return $handle->fetchall_arrayref(); + } + $table{handle} = undef; + } + return undef; +} + +sub refresh_activity +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @process_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $process_instances[($i*2)] = "$result->[$i][2]"; + $tableref = $result->[$i]; + $process_instances[($i*2)+1] = "$tableref->[2] $tableref->[5]"; + # 9.0 does not have client_hostname, deal with that first + splice @$tableref, 7, 0, (undef) unless (@$tableref > 13); + # special case needed for 'client_*' columns (6 -> 8), may be null + for my $j (6 .. 8) { # for each special case column + $tableref->[$j] = '' unless (defined($tableref->[$j])); + } + $table{values}{$instid} = $tableref; + } + } + $pmda->replace_indom($process_indom, \@process_instances); +} + +sub refresh_replication +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @replicant_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $replicant_instances[($i*2)] = "$result->[$i][2]"; + $replicant_instances[($i*2)+1] = "$result->[$i][2] $result->[$i][5]"; + # special case needed for 'client_*' columns (4 -> 5) + for my $j (4 .. 5) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($replicant_indom, \@replicant_instances); +} + +sub refresh_bgwriter +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + $table{values}{"$pm_in_null"} = \@{$result->[0]} unless (!defined($result)); +} + +sub refresh_active_functions +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + # Results from SQL (2 values; 2nd may well contain undef): + # select pg_is_in_recovery(), pg_current_xlog_location(), + # We need to split out the log identifier and log record offset in the + # final value, and save separately into the cached result table. + # + if (defined($result)) { + my $arrayref = $result->[0]; + my @values = @$arrayref; + my @massaged = ( $values[0], undef,undef ); + my $moffset = 1; # index for @massaged array + + if (defined($values[1])) { + my @pieces = split /\//, $values[1]; + $massaged[$moffset++] = hex($pieces[0]); + $massaged[$moffset++] = hex($pieces[1]); + } + $table{values}{"$pm_in_null"} = \@massaged; + } +} + +sub refresh_recovery_functions +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + # Results from SQL (3 values; 3rd,4th may well contain undef): + # select pg_is_in_recovery(), + # pg_last_xlog_receive_location(), pg_last_xlog_replay_location() + # We need to split out the log identifier and log record offset in the + # final values, and save separately into the cached result table. + # + if (defined($result)) { + my $arrayref = $result->[0]; + my @values = @$arrayref; + my @massaged = ( $values[0], undef,undef, undef,undef ); + my $moffset = 1; # index for @massaged array + + foreach my $voffset (1..2) { # index for @values array + if (defined($values[$voffset])) { + my @pieces = split /\//, $values[$voffset]; + $massaged[$moffset++] = hex($pieces[0]); + $massaged[$moffset++] = hex($pieces[1]); + } + } + $table{values}{"$pm_in_null"} = \@massaged; + } +} + +sub refresh_database +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @database_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $database_instances[($i*2)] = $result->[$i][0]; + $database_instances[($i*2)+1] = $result->[$i][1]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($database_indom, \@database_instances); +} + +sub refresh_user_functions +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @function_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $function_instances[($i*2)] = $result->[$i][0]; + $function_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($function_indom, \@function_instances); +} + +sub refresh_all_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_rel_instances[($i*2)] = $result->[$i][0]; + $all_rel_instances[($i*2)+1] = $result->[$i][2]; + # special case needed for 'last_*' columns (13 -> 16) + for my $j (13 .. 16) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_rel_indom, \@all_rel_instances); +} + +sub refresh_sys_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_rel_instances[($i*2)] = $result->[$i][0]; + $sys_rel_instances[($i*2)+1] = $result->[$i][2]; + # special case needed for 'last_*' columns (13 -> 16) + for my $j (13 .. 16) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_rel_indom, \@sys_rel_instances); +} + +sub refresh_user_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_rel_instances[($i*2)] = $result->[$i][0]; + $user_rel_instances[($i*2)+1] = $result->[$i][2]; + # special case needed for 'last_*' columns (13 -> 16) + for my $j (13 .. 16) { # for each special case column + $result->[$i][$j] = '' unless (defined($result->[$i][$j])); + } + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_rel_indom, \@user_rel_instances); +} + +sub refresh_xact_all_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_rel_instances[($i*2)] = $result->[$i][0]; + $all_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_rel_indom, \@all_rel_instances); +} + +sub refresh_xact_sys_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_rel_instances[($i*2)] = $result->[$i][0]; + $sys_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_rel_indom, \@sys_rel_instances); +} + +sub refresh_xact_user_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_rel_instances[($i*2)] = $result->[$i][0]; + $user_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_rel_indom, \@user_rel_instances); +} + +sub refresh_all_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_index_instances[($i*2)] = $result->[$i][1]; + $all_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_index_indom, \@all_index_instances); +} + +sub refresh_sys_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_index_instances[($i*2)] = $result->[$i][1]; + $sys_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_index_indom, \@sys_index_instances); +} + +sub refresh_user_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_index_instances[($i*2)] = $result->[$i][1]; + $user_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_index_indom, \@user_index_instances); +} + +sub refresh_io_all_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_rel_instances[($i*2)] = $result->[$i][0]; + $all_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_rel_indom, \@all_rel_instances); +} + +sub refresh_io_sys_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_rel_instances[($i*2)] = $result->[$i][0]; + $sys_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_rel_indom, \@sys_rel_instances); +} + +sub refresh_io_user_tables +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_rel_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_rel_instances[($i*2)] = $result->[$i][0]; + $user_rel_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_rel_indom, \@user_rel_instances); +} + +sub refresh_io_all_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_index_instances[($i*2)] = $result->[$i][1]; + $all_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_index_indom, \@all_index_instances); +} + +sub refresh_io_sys_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_index_instances[($i*2)] = $result->[$i][1]; + $sys_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_index_indom, \@sys_index_instances); +} + +sub refresh_io_user_indexes +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_index_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_index_instances[($i*2)] = $result->[$i][1]; + $user_index_instances[($i*2)+1] = $result->[$i][4]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_index_indom, \@user_index_instances); +} + +sub refresh_io_all_sequences +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @all_seq_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $all_seq_instances[($i*2)] = $result->[$i][0]; + $all_seq_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($all_seq_indom, \@all_seq_instances); +} + +sub refresh_io_sys_sequences +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @sys_seq_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $sys_seq_instances[($i*2)] = $result->[$i][0]; + $sys_seq_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($sys_seq_indom, \@sys_seq_instances); +} + +sub refresh_io_user_sequences +{ + my $tableref = shift; + my $result = refresh_results($tableref); + my %table = %$tableref; + + @user_seq_instances = (); # refresh indom too + if (defined($result)) { + for my $i (0 .. $#{$result}) { # for each row (instance) returned + my $instid = $user_seq_instances[($i*2)] = $result->[$i][0]; + $user_seq_instances[($i*2)+1] = $result->[$i][2]; + $table{values}{$instid} = $result->[$i]; + } + } + $pmda->replace_indom($user_seq_indom, \@user_seq_instances); +} + +# +# Setup routines - one per cluster, add metrics to PMDA +# +# For help text, see +# http://www.postgresql.org/docs/9.2/static/monitoring-stats.html +# +# and the Postgres Licence: +# Portions Copyright (c) 1996-2012, The PostgreSQL Global Development Group +# +# Portions Copyright (c) 1994, The Regents of the University of California +# +# Permission to use, copy, modify, and distribute this software +# and its documentation for any purpose, without fee, and without a +# written agreement is hereby granted, provided that the above copyright +# notice and this paragraph and the following two paragraphs appear in +# all copies. +# +# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +# INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND +# ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE +# PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY +# OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, +# UPDATES, ENHANCEMENTS, OR MODIFICATIONS +# + +sub setup_activity +{ + my ($cluster, $indom) = @_; + + # indom: procpid + application_name + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.datid', + 'OID of the database this backend is connected to', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.datname', + 'Name of the database this backend is connected to', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.usesysid', + 'OID of the user logged into this backend', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.usename', + 'Name of the user logged into this backend', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.application_name', + 'Name of the application that is connected to this backend', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.client_addr', + 'Client IP address', +'IP address of the client connected to this backend. If this field is null, +it indicates either that the client is connected via a Unix socket on the +server machine or that this is an internal process such as autovacuum.'); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.client_hostname', + 'Host name of the connected client', +'Client host name is derived from reverse DNS lookup of +postgresql.stat.activity.client_addr. This field will only be non-null +for IP connections, and only when log_hostname is enabled.'); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.client_port', + 'Client TCP port number', +'TCP port number that the client is using for communication with this +backend. Value will be -1 if a Unix socket is used.'); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.backend_start', + 'Time when this process was started', +'Time when this client process connected to the server.'); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.xact_start', + 'Transaction start time', +'Time when this process' . "'" . ' current transaction was started. +The value will be null if no transaction is active. If the +current query is the first of its transaction, value is equal to +postgresql.stat.activity.query_start.'); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.query_start', + 'Query start time', +'Time when the currently active query was started, or if state is not +active, when the last query was started'); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.waiting', + 'True if this backend is currently waiting on a lock', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.activity.current_query', + 'Most recent query', +'Text of this backend' . "'" . 's most recent query. If state is active this field +shows the currently executing query. In all other states, it shows the +last query that was executed.'); +} + +sub setup_replication +{ + my ($cluster, $indom) = @_; + + # indom: procpid + application_name + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.usesysid', + 'OID of the user logged into this WAL sender process', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.usename', + 'Name of the user logged into this WAL sender process', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.application_name', + 'Name of the application that is connected to this WAL sender', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.client_addr', + 'WAL client IP address', +'IP address of the client connected to this WAL sender. If this field is +null, it indicates that the client is connected via a Unix socket on the +server machine.'); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.client_hostname', + 'WAL client host name', +'Host name of the connected client, as reported by a reverse DNS lookup of +postgresql.stat.replication.client_addr. This field will only be non-null +for IP connections, and only when log_hostname is enabled.'); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.client_port', + 'WAL client TCP port', +'TCP port number that the client is using for communication with this WAL +sender, or -1 if a Unix socket is used.'); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.backend_start', + 'Time when when the client connected to this WAL sender', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.state', + 'Current WAL sender state', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.sent_location', + 'Last transaction log position sent on this connection', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.write_location', + 'Last transaction log position written to disk by this standby server', ''); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.flush_location', + 'Last transaction log position flushed to disk by this standby server', ''); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.replay_location', + 'Last transaction log position replayed into the database on this standby server', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.sync_priority', + 'Priority of this standby server for being chosen as the synchronous standby', ''); + $pmda->add_metric(pmda_pmid($cluster,14), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.replication.sync_state', + 'Synchronous state of this standby server', ''); +} + +sub setup_stat_xact_tables +{ + my ($cluster, $indom, $tables) = @_; + + # indom: relid + relname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.schemaname", + 'Name of the schema that this table is in', +''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.seq_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.seq_tup_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.idx_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.idx_tup_fetch", + 'Number of rows fetched by queries in this database', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_ins", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_upd", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_del", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.xact.$tables.n_tup_hot_upd", + '', ''); +} + +sub setup_stat_tables +{ + my ($cluster, $indom, $tables) = @_; + + # indom: relid + relname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.seq_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.seq_tup_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.idx_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.idx_tup_fetch", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_ins", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_upd", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_del", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_tup_hot_upd", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_live_tup", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.n_dead_tup", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_vacuum", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,14), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_autovacuum", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,15), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_analyze", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,16), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$tables.last_autoanalyze", + '', ''); +} + +sub setup_bgwriter +{ + my ($cluster, $indom) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.checkpoints_timed', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.checkpoints_req', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_checkpoints', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_clean', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.maxwritten_clean', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_backend', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.bgwriter.buffers_alloc', + '', ''); +} + +sub setup_active_functions +{ + my ($cluster, $indom) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.active.is_in_recovery', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.active.xlog_current_location_log_id', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.active.xlog_current_location_offset', + '', ''); +} + +sub setup_recovery_functions +{ + my ($cluster, $indom) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.is_in_recovery', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_receive_location_log_id', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_receive_location_offset', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_replay_location_log_id', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U64, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.recovery.xlog_replay_location_offset', + '', ''); +} + +sub setup_database_conflicts +{ + my ($cluster, $indom) = @_; + + # indom: datid + datname + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.tablespace', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.lock', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.snapshot', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.bufferpin', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database_conflicts.deadlock', + '', ''); + +} +sub setup_database +{ + my ($cluster, $indom) = @_; + + # indom: datid + datname + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_U32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.database.numbackends', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.xact_commit', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.xact_rollback', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.blks_read', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.blks_hit', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_returned', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_fetched', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_inserted', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_updated', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,11), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.tup_deleted', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,12), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'postgresql.stat.database.conflicts', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,13), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.database.stats_reset', + '', ''); +} + +sub setup_stat_indexes +{ + my ($cluster, $indom, $indexes) = @_; + + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$indexes.relid", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$indexes.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.stat.$indexes.relname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.stat.$indexes.idx_scan", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.stat.$indexes.idx_tup_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.stat.$indexes.idx_tup_fetch", + '', ''); +} + +sub setup_statio_tables +{ + my ($cluster, $indom, $tables) = @_; + + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$tables.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.heap_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.heap_blks_hit", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.idx_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.idx_blks_hit", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,7), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.toast_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,8), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.toast_blks_hit", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,9), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.tidx_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,10), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$tables.tidx_blks_hit", + '', ''); +} + +sub setup_statio_indexes +{ + my ($cluster, $indom, $indexes) = @_; + + # indom: indexrelid + indexrelname + $pmda->add_metric(pmda_pmid($cluster,0), PM_TYPE_32, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$indexes.relid", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,2), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$indexes.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$indexes.relname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$indexes.idx_blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,6), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$indexes.idx_blks_hit", + '', ''); +} + +sub setup_statio_sequences +{ + my ($cluster, $indom, $sequences) = @_; + + # indom: relid + relname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + "postgresql.statio.$sequences.schemaname", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$sequences.blks_read", + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U32, $indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + "postgresql.statio.$sequences.blks_hit", + '', ''); +} + +sub setup_xact_user_functions +{ + my ($cluster, $indom) = @_; + + # indom: funcid + funcname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.xact.user_functions.schemaname', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.xact.user_functions.calls', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.xact.user_functions.total_time', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.xact.user_functions.self_time', + '', ''); +} + +sub setup_user_functions +{ + my ($cluster, $indom) = @_; + + # indom: funcid + funcname + $pmda->add_metric(pmda_pmid($cluster,1), PM_TYPE_STRING, $indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.user_functions.schemaname', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,3), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,0,0,0,0,0), + 'postgresql.stat.user_functions.calls', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,4), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.user_functions.total_time', + '', ''); + $pmda->add_metric(pmda_pmid($cluster,5), PM_TYPE_U64, $indom, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'postgresql.stat.user_functions.self_time', + '', ''); +} + +# +# Main PMDA thread of execution starts here - setup metrics and callbacks +# drop root priveleges, then call PMDA 'run' routine to talk to pmcd. +# +$pmda = PCP::PMDA->new('postgresql', 110); +postgresql_metrics_setup(); +postgresql_indoms_setup(); + +$pmda->set_fetch_callback(\&postgresql_fetch_callback); +$pmda->set_fetch(\&postgresql_connection_setup); +$pmda->set_refresh(\&postgresql_refresh); +$pmda->set_user('postgres'); +$pmda->run; + +=pod + +=head1 NAME + +pmdapostgresql - PostgreSQL database PMDA + +=head1 DESCRIPTION + +B is a Performance Co-Pilot PMDA which extracts +live performance data from a running PostgreSQL database. + +=head1 INSTALLATION + +B uses a configuration file from (in this order): + +=over + +=item * /etc/pcpdbi.conf + +=item * $PCP_PMDAS_DIR/postgresql/postgresql.conf + +=back + +This file can contain overridden values (Perl code) for the settings +listed at the start of pmdapostgresql.pl, namely: + +=over + +=item * database name (see DBI(3) for details) + +=item * database username + +=back + +Once this is setup, you can access the names and values for the +postgresql performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/postgresql + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/postgresql + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item /etc/pcpdbi.conf + +configuration file for all PCP database monitors + +=item $PCP_PMDAS_DIR/postgresql/postgresql.conf + +configuration file for B + +=item $PCP_PMDAS_DIR/postgresql/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/postgresql/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/postgresql.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), pmdadbping.pl(1) and DBI(3). diff --git a/src/pmdas/postgresql/pmlogconf.summary b/src/pmdas/postgresql/pmlogconf.summary new file mode 100644 index 0000000..d278918 --- /dev/null +++ b/src/pmdas/postgresql/pmlogconf.summary @@ -0,0 +1,8 @@ +#pmlogconf-setup 2.0 +ident postgresql summary information +probe postgresql.stat.all_tables.seq_scan ? include : exclude + postgresql.stat.database + postgresql.stat.all_tables + postgresql.stat.all_indexes + postgresql.statio.all_tables + postgresql.statio.all_indexes diff --git a/src/pmdas/process/GNUmakefile b/src/pmdas/process/GNUmakefile new file mode 100644 index 0000000..b04c78f --- /dev/null +++ b/src/pmdas/process/GNUmakefile @@ -0,0 +1,65 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + + +IAM = process +DOMAIN = PROCESS +TARGETS = $(IAM) +CFILES = process.c +SCRIPTS = Install Remove +DFILES = README +LSRCFILES= $(SCRIPTS) pmns help root $(DFILES) process.conf + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMCHART = $(PCP_VAR_DIR)/config/pmchart + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so $(TARGETS) \ + help.dir help.pag +LLDLIBS = $(PCP_PMDALIB) + +default: build-me + +include $(BUILDRULES) + +# This PMDA is only valid on platforms with a procfs +# It is also superceded by the cgroup functionality on Linux +# thus has not been built for some time, for reference only. +#build-me: $(TARGETS) +#install install_pcp : default +# $(INSTALL) -m 755 -d $(PMDADIR) +# $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) +# $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) +# $(INSTALL) -m 644 $(DFILES) pmns help root domain.h $(PMDADIR) +# $(INSTALL) -m 644 process.conf $(PMDADIR)/process.conf +#else +build-me: +install: +#endif + +$(IAM): $(OBJECTS) + +default_pcp : default + +install_pcp : install + +process.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/process/Install b/src/pmdas/process/Install new file mode 100755 index 0000000..7bc54b9 --- /dev/null +++ b/src/pmdas/process/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2001 Alan Bailey. All Rights Reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the process PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=process +pmda_interface=2 +forced_restart=true + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/process/README b/src/pmdas/process/README new file mode 100644 index 0000000..78579e8 --- /dev/null +++ b/src/pmdas/process/README @@ -0,0 +1,74 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# + +Process PMDA +============ + +This PMDA monitors root processes to see if they are still running. +The processes that are monitored are stored in a config file, +and the PMDA returns the number of root processes with that name +running. In this way, you can check to see if the number of processes +is in a range, is 1, is 0, or some other combination. + +This source code was contributed by Alan Bailey (abailey@ncsa.uiuc.edu) +to the PCP open source project. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT process + +Installation +============ + + + # cd $PCP_PMDAS_DIR/process + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use + ($PCP_PMCDCONF_PATH). If there is, edit ./domain.h + to choose another domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + + + Alternatively, to install just the Performance Metrics Name Space + for the process metrics on the local system, but not the process + PMDA (presumably because the local system is running PCP 1.x and + you wish to connect to a remote system where PCP 2.0 and the process + PMDA is running), make sure the Performance Metrics Domain defined + in ./domain.h matches the domain chosen for the process PMDA on the + remote system (check the second field in the corresponding line of the + $PCP_PMCDCONF_PATH file on the remote system), then + + # ./Install -N + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/process + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/process.log) should be checked for any warnings or + errors. diff --git a/src/pmdas/process/Remove b/src/pmdas/process/Remove new file mode 100755 index 0000000..b90a863 --- /dev/null +++ b/src/pmdas/process/Remove @@ -0,0 +1,41 @@ +#! /bin/sh +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the process PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=process + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/process/help b/src/pmdas/process/help new file mode 100644 index 0000000..acf2a37 --- /dev/null +++ b/src/pmdas/process/help @@ -0,0 +1,38 @@ +# +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# process PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ process.running Tool for tracking running root processes +This tracks the number of running root processes for processes you define diff --git a/src/pmdas/process/pmns b/src/pmdas/process/pmns new file mode 100644 index 0000000..8df2327 --- /dev/null +++ b/src/pmdas/process/pmns @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) + * for the portions of the code supporting the initial agent functionality. + * All rights reserved. + * Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Metrics for process PMDA + */ + +process { + running PROCESS:0:0 +} diff --git a/src/pmdas/process/process.c b/src/pmdas/process/process.c new file mode 100644 index 0000000..00f9d80 --- /dev/null +++ b/src/pmdas/process/process.c @@ -0,0 +1,413 @@ +/* + * The process PMDA + * + * This code monitors root processes to see if they are still + * running. The processes that are monitored are stored in a config file, + * and the PMDA returns the number of root processes with that name + * running. In this way, you can check to see if the number of processes + * is in a range, is 1, is 0, or some other combination. + * + * Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) + * for the portions of the code supporting the initial agent functionality. + * All rights reserved. + * + * Copyright (c) 2001,2003,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include +#include +#include + +/* + * Process PMDA + * + * Metrics + * process.running + * The number of root processes running for each name + * The process names are stored in + * PCP_VAR_DIR/pmdas/process/process.conf + */ + +static pmdaInstid *processes; + +static pmdaIndom indomtab[] = { +#define PROC_INDOM 0 + { PROC_INDOM, 0, NULL } +}; + +static pmdaMetric metrictab[] = { +/* process.running */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_DOUBLE, PROC_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +/* Variables needed for this file */ +static int *num_procs; +static struct stat file_change; +static int npidlist; +static int maxpidlist; +static int *pidlist; +static int isDSO = 1; +static char mypath[MAXPATHLEN]; + +/* Routines in this file */ +static void process_clear_config_info(void); +static void process_grab_config_info(void); +static void process_config_file_check(void); +static int process_refresh_pidlist(void); + +static void +process_config_file_check(void) +{ + struct stat statbuf; + static int last_error; + int sep = __pmPathSeparator(); + + snprintf(mypath, sizeof(mypath), "%s%c" "process" "%c" "process.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if (stat(mypath, &statbuf) == -1) { + if (oserror() != last_error) { + last_error = oserror(); + __pmNotifyErr(LOG_WARNING, "stat failed on %s: %s\n", + mypath, pmErrStr(-oserror())); + } + } else { + last_error = 0; +#if defined(HAVE_ST_MTIME_WITH_E) + if (memcmp(&statbuf.st_mtime, &file_change.st_mtime, sizeof(statbuf.st_mtime)) != 0) +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + if (memcmp(&statbuf.st_mtimespec, &file_change.st_mtimespec, sizeof(statbuf.st_mtimespec)) != 0) +#else + if (memcmp(&statbuf.st_mtim, &file_change.st_mtim, sizeof(statbuf.st_mtim)) != 0) +#endif + { + process_clear_config_info(); + process_grab_config_info(); + file_change = statbuf; + } + } +} + +/* + * This function is called when the config file needs to be read in + * again, so we can define new instances and such. This frees the + * previous memory, if any (on startup, there won't be any), and sets + * the two variables to NULL for fun. + */ +static void +process_clear_config_info(void) +{ + int i; + + /* Free the memory holding the process name */ + for (i = 0; i < indomtab[PROC_INDOM].it_numinst; i++) + free(processes[i].i_name); + + /* Free the processes structure */ + if (processes) + free(processes); + + /* Free the process counter structure */ + if (num_procs) + free(num_procs); + num_procs = NULL; + + indomtab[PROC_INDOM].it_set = processes = NULL; + indomtab[PROC_INDOM].it_numinst = 0; +} + +/* + * This routine opens the config file and stores the information in the + * processes structure. The processes structure must be reallocated as + * necessary, and also the num_procs structure needs to be reallocated + * as we define new processes. When all of that is done, we fill in the + * values in the indomtab structure, those being the number of instances + * and the pointer to the processes structure. + * + * A lot of this code was taken from the simple.c code, but in this + * case, I changed a lot of it to fit my purposes... + */ +static void +process_grab_config_info(void) +{ + FILE *fp; + char process_name[1024]; + char *q; + size_t size; + int process_number = 0; + int sep = __pmPathSeparator(); + + snprintf(mypath, sizeof(mypath), "%s%c" "process" "%c" "process.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if ((fp = fopen(mypath, "r")) == NULL) { + __pmNotifyErr(LOG_ERR, "fopen on %s failed: %s\n", + mypath, pmErrStr(-oserror())); + if (processes != NULL) { + free(processes); + processes = NULL; + process_number = 0; + } + goto done; + } + + while (fgets(process_name, sizeof(process_name), fp) != NULL) { + if (process_name[0] == '#') + continue; + /* Remove the newline */ + if ((q = strchr(process_name, '\n')) != NULL) { + *q = '\0'; + } else { + /* This means the line was too long */ + __pmNotifyErr(LOG_WARNING, "line %d in config file too long\n", + process_number + 1); + } + size = (process_number + 1) * sizeof(pmdaInstid); + if ((processes = realloc(processes, size)) == NULL) + __pmNoMem("process", size, PM_FATAL_ERR); + processes[process_number].i_name = malloc(strlen(process_name) + 1); + strcpy(processes[process_number].i_name, process_name); + processes[process_number].i_inst = process_number; + process_number++; + } + fclose(fp); + +done: + if (processes == NULL) + __pmNotifyErr(LOG_WARNING, "\"process\" instance domain is empty"); + + indomtab[PROC_INDOM].it_set = processes; + indomtab[PROC_INDOM].it_numinst = process_number; + + num_procs = realloc(num_procs, (process_number)*sizeof(int)); +} + +/* + * This code refreshes the count of all the processes that we want to + * monitor. It does this by calling process_refresh_pidlist, which just + * grabs a list of all the running processes (as PIDs). We then loop + * through each of these processes, and check to see if it is a root + * process. If so, we then open up the /proc//status file, + * and see what the process name is. If it is one of our special + * processes that we want to track, then we up the count for that + * process in num_procs. Then we're done. + */ +static void +process_refresh_pid_checks(void) +{ + int proc; + char proc_path[30]; + char cmd_line[200]; + FILE *fd; + int proc_name; + int item; + struct stat statbuf; + + process_refresh_pidlist(); + + /* Clear the variables */ + for (item = 0; item < indomtab[PROC_INDOM].it_numinst; item++) + num_procs[item] = 0; + + for (proc = 0; proc < npidlist; proc++) { + sprintf(proc_path, "/proc/%d/status", pidlist[proc]); + if (stat(proc_path, &statbuf) == -1) { + /* We can't stat it, let's assume the process isn't running anymore */ + continue; + } else { + /* This checks to make sure the process is a root process */ + if (statbuf.st_uid != 0) { + continue; + } + } + + if ((fd = fopen(proc_path, "r")) != NULL) { + /* + * matching process by name is platform specific ... this code + * works for Linux when /proc//status is ascii and the + * first line is of the form: + * Name: + */ + if (fgets(cmd_line, sizeof(cmd_line), fd) == NULL) { + /* + * not expected, but not a disaster ... we'll just not try + * to match this one + */ + fclose(fd); + continue; + } + fclose(fd); + + for (proc_name = 0; proc_name < indomtab[PROC_INDOM].it_numinst; proc_name++) { + if (strstr(cmd_line, (processes[proc_name]).i_name)) { + (num_procs[proc_name])++; + } + } + } + } +} + +/* + * Read in the list of all processes running. + */ +int +process_refresh_pidlist() +{ + DIR *dirp = opendir("/proc"); + struct dirent *dp; + + if (dirp == NULL) + return -oserror(); + + npidlist = 0; + while ((dp = readdir(dirp)) != (struct dirent *)NULL) { + if (isdigit((int)dp->d_name[0])) { + if (npidlist >= maxpidlist) { + maxpidlist += 16; + pidlist = (int *)realloc(pidlist, maxpidlist * sizeof(int)); + } + pidlist[npidlist++] = atoi(dp->d_name); + } + } + closedir(dirp); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "process_refresh_pidlist: found %d PIDs:", npidlist); + if (npidlist > 0) { + fprintf(stderr, " %d", pidlist[0]); + if (npidlist > 1) { + fprintf(stderr, " %d", pidlist[1]); + if (npidlist == 3) + fprintf(stderr, " %d", pidlist[2]); + else if (npidlist == 4) + fprintf(stderr, " %d %d", pidlist[2], pidlist[3]); + else if (npidlist > 4) + fprintf(stderr, " ... %d %d", pidlist[npidlist-2], + pidlist[npidlist-1]); + } + } + fprintf(stderr, "\n"); + } +#endif + + return npidlist; +} + +static int +process_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + /* + * This is the wrapper over the pmdaFetch routine, to handle the problem + * of varying instance domains. All this does is check to see if the + * config file has been updated, and update the instance domain if so, + * and refresh the pid count for the processes. + */ + process_config_file_check(); + process_refresh_pid_checks(); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + + +/* + * callback provided to pmdaFetch + */ +static int +process_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (idp->cluster != 0) + return PM_ERR_PMID; + if (idp->item != 0) + return PM_ERR_PMID; + + /* We have the values stored, simply grab it and return it. */ + atom->d = num_procs[inst]; + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +process_init(pmdaInterface *dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "process" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_2, "process DSO", mypath); + } + + if (dp->status != 0) + return; + + dp->version.two.fetch = process_fetch; + + /* Let's grab the info right away just to make sure it's there. */ + process_grab_config_info(); + + pmdaSetFetchCallBack(dp, process_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface desc; + + isDSO = 0; + __pmSetProgname(argv[0]); + + snprintf(mypath, sizeof(mypath), "%s%c" "process" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, PROCESS, + "process.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + pmdaOpenLog(&desc); + process_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + exit(0); +} diff --git a/src/pmdas/process/process.conf b/src/pmdas/process/process.conf new file mode 100644 index 0000000..a02879d --- /dev/null +++ b/src/pmdas/process/process.conf @@ -0,0 +1,11 @@ +# Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) +# for the portions of the code supporting the initial agent functionality. +# All rights reserved. +# Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +sshd +crond +inetd +nfsd +afsd +test diff --git a/src/pmdas/process/root b/src/pmdas/process/root new file mode 100644 index 0000000..f2fba58 --- /dev/null +++ b/src/pmdas/process/root @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2001 Alan Bailey (bailey@mcs.anl.gov or abailey@ncsa.uiuc.edu) + * for the portions of the code supporting the initial agent functionality. + * All rights reserved. + * Copyright (c) 2001,2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +/* + * fake "root" for validating the local PMNS subtree + */ +#include + +root { process } + +#include "pmns" + diff --git a/src/pmdas/roomtemp/GNUmakefile b/src/pmdas/roomtemp/GNUmakefile new file mode 100644 index 0000000..e749011 --- /dev/null +++ b/src/pmdas/roomtemp/GNUmakefile @@ -0,0 +1,66 @@ +# +# Copyright (c) 2000-2001,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +HFILES = dsread.h +CFILES = roomtemp.c dsread.c +HFILES = dsread.h +CMDTARGET = pmdaroomtemp +LLDFLAGS = -L. +LLDLIBS = -lmlan $(PCP_PMDALIB) +LCFLAGS = -I. +DFILES = README help +LSRCFILES = Install Remove pmns $(DFILES) root + +SUBDIRS = mlan + +IAM = roomtemp +DOMAIN = ROOMTEMP +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log pmda$(IAM) pmda_$(IAM).so libmlan.a + +default: build-me + +include $(BUILDRULES) + +.NOTPARALLEL: + +ifeq "$(findstring $(TARGET_OS),solaris linux)" "" +build-me: + $(SUBDIRS_MAKERULE) + +install: +else +build-me: domain.h libmlan.a $(CMDTARGET) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root pmns domain.h $(PMDADIR) +endif + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +libmlan.a : $(SUBDIRS) + $(SUBDIRS_MAKERULE) + rm -f libmlan.a + ln -sf mlan/libmlan.a . + +default_pcp : default + +install_pcp : install diff --git a/src/pmdas/roomtemp/Install b/src/pmdas/roomtemp/Install new file mode 100644 index 0000000..02c6759 --- /dev/null +++ b/src/pmdas/roomtemp/Install @@ -0,0 +1,55 @@ +#! /bin/sh +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the roomtemp PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=roomtemp +pmda_interface=2 +forced_restart=true +pmdaSetup + +if $do_pmda +then + if [ -c /dev/ttyf1 ] + then + # reasonable first guess for IRIX + # + tty=/dev/ttyf1 + elif [ -c /dev/ttyS0 ] + then + # reasonable first guess for COM port on a Linux laptop + # + tty=/dev/ttyS0 + fi + + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N "Serial line for Dallas device? ""$PCP_ECHO_C" + [ ! -z "$tty" ] && $PCP_ECHO_PROG $PCP_ECHO_N "[$tty] ""$PCP_ECHO_C" + read ans + [ ! -z "$ans" ] && tty="$ans" + [ ! -z "$tty" ] && break + echo + echo "Ahem, you must answer the question ..." + done + args="$tty" +fi + +pmdaInstall + +exit 0 diff --git a/src/pmdas/roomtemp/README b/src/pmdas/roomtemp/README new file mode 100644 index 0000000..d347308 --- /dev/null +++ b/src/pmdas/roomtemp/README @@ -0,0 +1,74 @@ +Roomtemp PMDA +============= + +This PMDA exports the temperature from one or more sensors built using +the DS2480 and DS1280 chipsets and MicroLAN (aka 1-Wire) technology +from Dallas Semiconductor Corporation. See http://www.dalsemi.com/ + +You'll need the following hardware: + +DS2480 Serial 1-Wire Line Driver + http://www.dalsemi.com/datasheets/pdfs/2480.pdf + + Not needed if you obtain one of the integrated packages like the + DS9097U. + +DS9097U Universal 1-Wire COM Port Adapter + http://www.dalsemi.com/datasheets/pdfs/9097u.pdf + + This includes the DS2480 and I have the DS9097U-S09 variant that + is a standard DB-9 female package. + +DS1820 1-Wire Digital Thermometer + http://www.dalsemi.com/datasheets/pdfs/1820.pdf + +After you've got at least one DS9097U and one DS1820 you'll need a +spare serial port on the host running PMCD, a soldering iron and some +cable (telco or similar) with an RJ-11 male plug attached at one end. + +Use the ./probe application to exercise all of the hardware before +you try and install the PMDA. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT roomtemp + +Installation +============ + + + # cd $PCP_PMDAS_DIR/roomtemp + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/roomtemp + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/roomtemp.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/roomtemp/Remove b/src/pmdas/roomtemp/Remove new file mode 100644 index 0000000..8dd557a --- /dev/null +++ b/src/pmdas/roomtemp/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the roomtemp PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=roomtemp + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/roomtemp/dsread.c b/src/pmdas/roomtemp/dsread.c new file mode 100644 index 0000000..5ac8474 --- /dev/null +++ b/src/pmdas/roomtemp/dsread.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +#include "mlan/mlan.h" + +//---------------------------------------------------------------------- +// Read the temperature of a DS1820 +// +// 'SerialNum' - Serial Number of DS1820 to read temperature from +// 'Temp ' - pointer to variable where that temperature will be +// returned +// +// Returns: TRUE(1) temperature has been read and verified +// FALSE(0) could not read the temperature, perhaps device is not +// in contact +// +int ReadTemperature(uchar SerialNum[8], float *Temp) +{ + int rt=FALSE; + uchar send_block[30]; + int send_cnt=0, tsht, i; + float tmp,cr,cpc; + DOWCRC = 0; + + // set the device serial number to the counter device + MLanSerialNum(SerialNum,FALSE); + + // access the device + if (MLanAccess()) + { + // send the convert temperature command + MLanTouchByte(0x44); + + // set the MicroLAN to strong pull-up + if (MLanLevel(MODE_STRONG5) != MODE_STRONG5) + return FALSE; + + // sleep for 1 second + msDelay(1000); + + // turn off the MicroLAN strong pull-up + if (MLanLevel(MODE_NORMAL) != MODE_NORMAL) + return FALSE; + + // access the device + if (MLanAccess()) + { + // create a block to send that reads the temperature + // read scratchpad command + send_block[send_cnt++] = 0xBE; + // now add the read bytes for data bytes and crc8 + for (i = 0; i < 9; i++) + send_block[send_cnt++] = 0xFF; + + // now send the block + if (MLanBlock(FALSE,send_block,send_cnt)) + { + // perform the CRC8 on the last 8 bytes of packet + for (i = send_cnt - 9; i < send_cnt; i++) + dowcrc(send_block[i]); + + // verify CRC8 is correct + if (DOWCRC == 0x00) + { + // calculate the high-res temperature + tsht = send_block[1]; + if (send_block[2] & 0x01) + tsht |= -256; + tmp = (float)(tsht/2); + cr = send_block[7]; + cpc = send_block[8]; + if (cpc == 0) + return FALSE; + else + tmp = tmp - (float)0.25 + (cpc - cr)/cpc; + + *Temp = tmp; + // success + rt = TRUE; + } + } + } + } + + // return the result flag rt + return rt; +} + +/* + * find next temperature sensor + */ +unsigned char * +nextsensor(void) +{ + static unsigned char sn[8]; + static int first = 1; + + if (first) { + // set the search to first find for temperature family code + MLanFamilySearchSetup(TEMP_FAMILY); + first = 0; + } + + for ( ; ; ) { + // perform the search + if (!MLanNext(TRUE, FALSE)) { + return NULL; + } + // get serial number and verify the family code + MLanSerialNum(sn, TRUE); + if (sn[0] == TEMP_FAMILY) + return sn; + } +} diff --git a/src/pmdas/roomtemp/dsread.h b/src/pmdas/roomtemp/dsread.h new file mode 100644 index 0000000..8251baa --- /dev/null +++ b/src/pmdas/roomtemp/dsread.h @@ -0,0 +1,4 @@ +#include "mlan/mlan.h" + +extern int ReadTemperature(uchar *, float *); +unsigned char *nextsensor(void); diff --git a/src/pmdas/roomtemp/help b/src/pmdas/roomtemp/help new file mode 100644 index 0000000..75a3e4d --- /dev/null +++ b/src/pmdas/roomtemp/help @@ -0,0 +1,41 @@ +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# roomtemp PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ ROOMTEMP.0 Instance domain "device" for roomtemp PMDA +One instance for each temperature sensor device. The external instance +identifiers are the serial numbers (in hexadecimal) of the DS1280 chips +that were discovered when the MicroLAN was probed. + +@ roomtemp.celsius Temperature in degrees celsius. +@ roomtemp.fahrenheit Temperature in degrees fahrenheit. + diff --git a/src/pmdas/roomtemp/mlan/GNUmakefile b/src/pmdas/roomtemp/mlan/GNUmakefile new file mode 100644 index 0000000..f6e33ae --- /dev/null +++ b/src/pmdas/roomtemp/mlan/GNUmakefile @@ -0,0 +1,45 @@ +#!gmake +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +CFILES=mlannetu.c mlansesu.c mlanllu.c linuxlnk.c ds2480ut.c mlantrnu.c +HFILES=mlan.h ds2480.h +STATICLIBTARGET = libmlan.a +LDIRT = libmlan.a +LCFLAGS = -g + +default : $(STATICLIBTARGET) + +ifeq "$(findstring $(TARGET_OS),solaris linux)" "" +default: +else +default : $(STATICLIBTARGET) +endif + +.NOTPARALLEL: + +install : + +default_pcp : default + +install_pcp : install + +include $(BUILDRULES) diff --git a/src/pmdas/roomtemp/mlan/ds2480.h b/src/pmdas/roomtemp/mlan/ds2480.h new file mode 100644 index 0000000..139e2c4 --- /dev/null +++ b/src/pmdas/roomtemp/mlan/ds2480.h @@ -0,0 +1,183 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// DS2480.H - This file contains the DS2480 constants +// +// Version: 1.03 +// History: 1.02 -> 1.03 Make sure uchar is not defined twice. +// +// + +// typedefs +#ifndef UCHAR + #define UCHAR + typedef unsigned char uchar; +#endif + +// Mode Commands +#define MODE_DATA 0xE1 +#define MODE_COMMAND 0xE3 +#define MODE_STOP_PULSE 0xF1 + +// Return byte value +#define RB_CHIPID_MASK 0x1C +#define RB_RESET_MASK 0x03 +#define RB_1WIRESHORT 0x00 +#define RB_PRESENCE 0x01 +#define RB_ALARMPRESENCE 0x02 +#define RB_NOPRESENCE 0x03 + +#define RB_BIT_MASK 0x03 +#define RB_BIT_ONE 0x03 +#define RB_BIT_ZERO 0x00 + +// Masks for all bit ranges +#define CMD_MASK 0x80 +#define FUNCTSEL_MASK 0x60 +#define BITPOL_MASK 0x10 +#define SPEEDSEL_MASK 0x0C +#define MODSEL_MASK 0x02 +#define PARMSEL_MASK 0x70 +#define PARMSET_MASK 0x0E + +// Command or config bit +#define CMD_COMM 0x81 +#define CMD_CONFIG 0x01 + +// Function select bits +#define FUNCTSEL_BIT 0x00 +#define FUNCTSEL_SEARCHON 0x30 +#define FUNCTSEL_SEARCHOFF 0x20 +#define FUNCTSEL_RESET 0x40 +#define FUNCTSEL_CHMOD 0x60 + +// Bit polarity/Pulse voltage bits +#define BITPOL_ONE 0x10 +#define BITPOL_ZERO 0x00 +#define BITPOL_5V 0x00 +#define BITPOL_12V 0x10 + +// One Wire speed bits +#define SPEEDSEL_STD 0x00 +#define SPEEDSEL_FLEX 0x04 +#define SPEEDSEL_OD 0x08 +#define SPEEDSEL_PULSE 0x0C + +// Data/Command mode select bits +#define MODSEL_DATA 0x00 +#define MODSEL_COMMAND 0x02 + +// 5V Follow Pulse select bits (If 5V pulse +// will be following the next byte or bit.) +#define PRIME5V_TRUE 0x02 +#define PRIME5V_FALSE 0x00 + +// Parameter select bits +#define PARMSEL_PARMREAD 0x00 +#define PARMSEL_SLEW 0x10 +#define PARMSEL_12VPULSE 0x20 +#define PARMSEL_5VPULSE 0x30 +#define PARMSEL_WRITE1LOW 0x40 +#define PARMSEL_SAMPLEOFFSET 0x50 +#define PARMSEL_ACTIVEPULLUPTIME 0x60 +#define PARMSEL_BAUDRATE 0x70 + +// Pull down slew rate. +#define PARMSET_Slew15Vus 0x00 +#define PARMSET_Slew2p2Vus 0x02 +#define PARMSET_Slew1p65Vus 0x04 +#define PARMSET_Slew1p37Vus 0x06 +#define PARMSET_Slew1p1Vus 0x08 +#define PARMSET_Slew0p83Vus 0x0A +#define PARMSET_Slew0p7Vus 0x0C +#define PARMSET_Slew0p55Vus 0x0E + +// 12V programming pulse time table +#define PARMSET_32us 0x00 +#define PARMSET_64us 0x02 +#define PARMSET_128us 0x04 +#define PARMSET_256us 0x06 +#define PARMSET_512us 0x08 +#define PARMSET_1024us 0x0A +#define PARMSET_2048us 0x0C +#define PARMSET_infinite 0x0E + +// 5V strong pull up pulse time table +#define PARMSET_16p4ms 0x00 +#define PARMSET_65p5ms 0x02 +#define PARMSET_131ms 0x04 +#define PARMSET_262ms 0x06 +#define PARMSET_524ms 0x08 +#define PARMSET_1p05s 0x0A +#define PARMSET_2p10s 0x0C +#define PARMSET_infinite 0x0E + +// Write 1 low time +#define PARMSET_Write8us 0x00 +#define PARMSET_Write9us 0x02 +#define PARMSET_Write10us 0x04 +#define PARMSET_Write11us 0x06 +#define PARMSET_Write12us 0x08 +#define PARMSET_Write13us 0x0A +#define PARMSET_Write14us 0x0C +#define PARMSET_Write15us 0x0E + +// Data sample offset and Write 0 recovery time +#define PARMSET_SampOff3us 0x00 +#define PARMSET_SampOff4us 0x02 +#define PARMSET_SampOff5us 0x04 +#define PARMSET_SampOff6us 0x06 +#define PARMSET_SampOff7us 0x08 +#define PARMSET_SampOff8us 0x0A +#define PARMSET_SampOff9us 0x0C +#define PARMSET_SampOff10us 0x0E + +// Active pull up on time +#define PARMSET_PullUp0p0us 0x00 +#define PARMSET_PullUp0p5us 0x02 +#define PARMSET_PullUp1p0us 0x04 +#define PARMSET_PullUp1p5us 0x06 +#define PARMSET_PullUp2p0us 0x08 +#define PARMSET_PullUp2p5us 0x0A +#define PARMSET_PullUp3p0us 0x0C +#define PARMSET_PullUp3p5us 0x0E + +// Baud rate bits +#define PARMSET_9600 0x00 +#define PARMSET_19200 0x02 +#define PARMSET_57600 0x04 +#define PARMSET_115200 0x06 + +// DS2480 program voltage available +#define DS2480PROG_MASK 0x20 + +// mode bit flags +#define MODE_NORMAL 0x00 +#define MODE_OVERDRIVE 0x01 +#define MODE_STRONG5 0x02 +#define MODE_PROGRAM 0x04 +#define MODE_BREAK 0x08 + diff --git a/src/pmdas/roomtemp/mlan/ds2480ut.c b/src/pmdas/roomtemp/mlan/ds2480ut.c new file mode 100644 index 0000000..d353743 --- /dev/null +++ b/src/pmdas/roomtemp/mlan/ds2480ut.c @@ -0,0 +1,206 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// ds2480ut.c - DS2480 utility functions. +// +// Version: 1.03 +// +// History: 1.00 -> 1.01 Default PDSRC changed from 0.83 to 1.37V/us +// in DS2480Detect. Changed to use msDelay instead +// of Delay. +// +// 1.01 -> 1.02 Changed global declarations from 'uchar' to 'int'. +// Changed DSO/WORT from 7 to 10us in DS2480Detect. +// +// 1.02 -> 1.03 Removed caps in #includes for Linux capatibility + +#include "ds2480.h" +#include "mlan.h" + +// external COM functions required +extern void FlushCOM(void); +extern int WriteCOM(int, uchar *); +extern int ReadCOM(int, uchar *); +extern void BreakCOM(void); +//extern void SetBaudCOM(uchar); +extern void msDelay(int); + +// exportable functions +int DS2480Detect(void); +int DS2480ChangeBaud(uchar); + +// global DS2480 state +int UMode = MODSEL_COMMAND; // current DS2480 command or data mode stateuchar +int UBaud = PARMSET_9600; // current DS2480 baud rate +int USpeed = SPEEDSEL_FLEX; // current DS2480 MicroLAN communication speed +int ULevel = MODE_NORMAL; // current DS2480 MicroLAN level + +//--------------------------------------------------------------------------- +// Attempt to resyc and detect a DS2480 +// +// Returns: TRUE - DS2480 detected successfully +// FALSE - Could not detect DS2480 +// +int DS2480Detect(void) +{ + uchar sendpacket[10],readbuffer[10]; + short sendlen=0; + + // reset modes + UMode = MODSEL_COMMAND; + UBaud = PARMSET_9600; + USpeed = SPEEDSEL_FLEX; + + // set the baud rate to 9600 + SetBaudCOM((uchar)UBaud); + + // send a break to reset the DS2480 + BreakCOM(); + + // delay to let line settle + msDelay(2); + + // flush the buffers + FlushCOM(); + + // send the timing byte + sendpacket[0] = 0xC1; + if (WriteCOM(1,sendpacket) != 1) + return FALSE; + + // set the FLEX configuration parameters + // default PDSRC = 1.37Vus + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_SLEW | PARMSET_Slew1p37Vus; + // default W1LT = 12us + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_WRITE1LOW | PARMSET_Write12us; + // default DSO/WORT = 10us + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_SAMPLEOFFSET | PARMSET_SampOff10us; + + // construct the command to read the baud rate (to test command block) + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_PARMREAD | (PARMSEL_BAUDRATE >> 3); + + // also do 1 bit operation (to test 1-Wire block) + sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_BIT | UBaud | BITPOL_ONE; + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the response + if (ReadCOM(5,readbuffer) == 5) + { + // look at the baud rate and bit operation + // to see if the response makes sense + if (((readbuffer[3] & 0xF1) == 0x00) && + ((readbuffer[3] & 0x0E) == UBaud) && + ((readbuffer[4] & 0xF0) == 0x90) && + ((readbuffer[4] & 0x0C) == UBaud)) + return TRUE; + } + } + + return FALSE; +} + + +//--------------------------------------------------------------------------- +// Change the DS2480 from the current baud rate to the new baud rate. +// +// 'newbaud' - the new baud rate to change to, defined as: +// PARMSET_9600 0x00 +// PARMSET_19200 0x02 +// PARMSET_57600 0x04 +// PARMSET_115200 0x06 +// +// Returns: current DS2480 baud rate. +// +int DS2480ChangeBaud(uchar newbaud) +{ + int rt=FALSE; + uchar readbuffer[5],sendpacket[5],sendpacket2[5]; + int sendlen=0,sendlen2=0; + + // see if diffenent then current baud rate + if (UBaud == newbaud) + return TRUE; + else + { + // build the command packet + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + // build the command + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_BAUDRATE | newbaud; + + // flush the buffers + FlushCOM(); + + // send the packet + if (!WriteCOM(sendlen,sendpacket)) + rt = FALSE; + else + { + // make sure buffer is flushed + msDelay(5); + + // change our baud rate + SetBaudCOM(newbaud); + UBaud = newbaud; + + // wait for things to settle + msDelay(5); + + // build a command packet to read back baud rate + sendpacket2[sendlen2++] = CMD_CONFIG | PARMSEL_PARMREAD | (PARMSEL_BAUDRATE >> 3); + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen2,sendpacket2)) + { + // read back the 1 byte response + if (ReadCOM(1,readbuffer) == 1) + { + // verify correct baud + if (((readbuffer[0] & 0x0E) == (sendpacket[sendlen-1] & 0x0E))) + rt = TRUE; + } + } + } + } + + // if lost communication with DS2480 then reset + if (rt != TRUE) + DS2480Detect(); + + return UBaud; +} diff --git a/src/pmdas/roomtemp/mlan/linuxlnk.c b/src/pmdas/roomtemp/mlan/linuxlnk.c new file mode 100644 index 0000000..aa20695 --- /dev/null +++ b/src/pmdas/roomtemp/mlan/linuxlnk.c @@ -0,0 +1,443 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// TODO.C - COM functions required by MLANLL.C, MLANTRNU, MLANNETU.C and +// MLanFile.C for MLANU to communicate with the DS2480 based +// Universal Serial Adapter 'U'. Fill in the platform specific code. +// +// Version: 1.02 +// +// History: 1.00 -> 1.01 Added function msDelay. +// +// 1.01 -> 1.02 Changed to generic OpenCOM/CloseCOM for easier +// use with other platforms. +// + +//-------------------------------------------------------------------------- +// Copyright (C) 1998 Andrea Chambers and University of Newcastle upon Tyne, +// All Rights Reserved. +//-------------------------------------------------------------------------- +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE UNIVERSITY OF NEWCASTLE UPON TYNE OR ANDREA CHAMBERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//--------------------------------------------------------------------------- +// +// LinuxLNK.C - COM functions required by MLANLLU.C, MLANTRNU.C, MLANNETU.C +// and MLanFile.C for MLANU to communicate with the DS2480 based +// Universal Serial Adapter 'U'. Platform specific code. +// +// Version: 1.03 +// History: 1.00 -> 1.03 modifications by David Smiczek +// Changed to use generic OpenCOM/CloseCOM +// Pass port name to OpenCOM instead of hard coded +// Changed msDelay to handle long delays +// Reformatted to look like 'TODO.C' +// Added #include "ds2480.h" to use constants. +// Added function SetBaudCOM() +// Added function msGettick() +// Removed delay from WriteCOM(), used tcdrain() +// Added wait for byte available with timeout using +// select() in ReadCOM() +/* + cfmakeraw function from nut-0.45.0 package + common.c - common useful functions + + Copyright (C) 2000 Russell Kroll + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mlan.h" +#include "ds2480.h" +#include "pmapi.h" + +// Exportable functions required for MLANLL.C, MLANTRNU.C, or MLANNETU.C +void FlushCOM(void); +int WriteCOM(int, unsigned char*); +int ReadCOM(int, unsigned char*); +void BreakCOM(void); +void msDelay(int); +long msGettick(void); + +// Exportable functions for opening/closing serial port +int OpenCOM(char *); +void CloseCOM(void); + +// LinuxLNK global +int fd; + +#ifdef IS_SOLARIS +int cfmakeraw(struct termios *termios_p) +{ + termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + termios_p->c_oflag &= ~OPOST; + termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + termios_p->c_cflag &= ~(CSIZE|PARENB); + termios_p->c_cflag |= CS8; + return 0; +} +#endif + +//-------------------------------------------------------------------------- +// Write an array of bytes to the COM port, verify that it was +// sent out. Assume that baud rate has been set. +// +// Returns 1 for success and 0 for failure +// +int WriteCOM(int outlen, uchar *outbuf) +{ + long count = outlen; + int i; + int sts; + + if (MLanDebug) { + fprintf(stderr, "WriteCOM: calling write: %d bytes:", outlen); + for (i = 0; i < outlen; i++) fprintf(stderr, " %02x", 0xff & outbuf[i]); + fputc('\n', stderr); + fflush(stderr); + } + + i = write(fd, outbuf, outlen); + + if (MLanDebug) { + fprintf(stderr, "WriteCOM: write returns %d\nWriteCOM: calling tcdrain\n", i); + fflush(stderr); + } + + sts = tcdrain(fd); + + if (MLanDebug) { + fprintf(stderr, "WriteCOM: tcdrain returns %d\n", sts); + fflush(stderr); + } + + return (i == count); +} + + +//-------------------------------------------------------------------------- +// Read an array of bytes to the COM port, verify that it was +// sent out. Assume that baud rate has been set. +// +// Returns number of characters read +// +int ReadCOM(int inlen, uchar *inbuf) +{ + fd_set filedescr; + struct timeval tval; + int cnt; + int sts; + + if (MLanDebug) { + fprintf(stderr, "ReadCOM: calling read: want %d bytes:", inlen); + fflush(stderr); + } + + // loop to wait until each byte is available and read it + for (cnt = 0; cnt < inlen; cnt++) + { + // set a descriptor to wait for a character available + FD_ZERO(&filedescr); + FD_SET(fd,&filedescr); + // set timeout to 10ms + tval.tv_sec = 0; + tval.tv_usec = 10000; + + // if byte available read or return bytes read + if (0 != select(fd+1,&filedescr,NULL,NULL,&tval)) { + if ((sts = read(fd,&inbuf[cnt],1)) != 1) { + if (MLanDebug) { + fprintf(stderr, ": read returns %d, got %d bytes\n", sts, cnt); + fflush(stderr); + } + return cnt; + } + if (MLanDebug) { + fprintf(stderr, " %02x", 0xff & inbuf[cnt]); + fflush(stderr); + } + } + else { + if (MLanDebug) { + fprintf(stderr, ": select timeout, got %d bytes\n", cnt); + fflush(stderr); + } + return cnt; + } + + } + + // success, so return desired length + if (MLanDebug) { + fprintf(stderr, ": got 'em all\n"); + fflush(stderr); + } + return inlen; +} + + +//--------------------------------------------------------------------------- +// Description: +// flush the rx and tx buffers +// +void FlushCOM(void) +{ + tcflush (fd, TCIOFLUSH); +} + + +//-------------------------------------------------------------------------- +// Description: +// Delay for at least 'len' ms +// +void msDelay(int len) +{ + struct timespec s; // Set aside memory space on the stack + + s.tv_sec = len / 1000; + s.tv_nsec = (len - (s.tv_sec * 1000)) * 1000000; + nanosleep(&s, NULL); +} + + +//-------------------------------------------------------------------------- +// Description: +// Send a break on the com port for at least 2 ms +// +void BreakCOM(void) +{ + int duration = 0; // see man termios break may be + tcsendbreak(fd, duration); // too long +} + + +//--------------------------------------------------------------------------- +// Attempt to open a com port. +// Set the starting baud rate to 9600. +// +// 'port_zstr' - zero terminate port name. Format is platform +// dependent. +// +// Returns: TRUE - success, COM port opened +// +int OpenCOM(char *port_zstr) +{ + struct termios t; // see man termios - declared as above + int rc; + + fd = open(port_zstr, O_RDWR|O_NONBLOCK); + if (fd<0) return fd; + rc = tcgetattr (fd, &t); + if (rc < 0) + { + int tmp; + tmp = oserror(); + close(fd); + setoserror(tmp); + return rc; + } + if (MLanDebug) { + fprintf(stderr, "OpenCOM: initial tty settings\niflag: %07o oflag: %07o lflag: %07o cflag: %07o\n", (unsigned int)t.c_iflag, (unsigned int)t.c_oflag, (unsigned int)t.c_lflag, (unsigned int)t.c_cflag); + fflush(stderr); + } + + cfsetospeed(&t, B9600); + cfsetispeed (&t, B9600); + +#ifdef IRIX +// IRIX tty games ... +// +// per the Linux man page cfmakeraw sets the terminal attributes as follows: +// + t.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + t.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON); + t.c_oflag &= ~OPOST; + t.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + t.c_cflag &= ~(CSIZE|PARENB); + t.c_cflag |= CS8; +// to this we have to clear the additional IRIX goodies like: +// + t.c_iflag &= ~(IXANY|IGNPAR); + t.c_oflag &= ~(TAB3); +// and finally, CLOCAL is in c_cflag, not c_lflag +// + t.c_cflag |= CLOCAL; // ignore modem signals +#else +// assumed to be Linux +// + cfmakeraw(&t); // don't generate signals, translate or echo + t.c_lflag |= CLOCAL; // ignore modem signals +#endif + + if (MLanDebug) { + fprintf(stderr, "OpenCOM: new tty settings\niflag: %07o oflag: %07o lflag: %07o cflag: %07o\n", (unsigned int)t.c_iflag, (unsigned int)t.c_oflag, (unsigned int)t.c_lflag, (unsigned int)t.c_cflag); + fprintf(stderr, "OpenCOM: calling tcsetattr\n"); + fflush(stderr); + } + rc = tcsetattr (fd, TCSAFLUSH, &t); + if (MLanDebug) { + fprintf(stderr, "OpenCOM: tcsetattr returns %d\n", rc); + fflush(stderr); + } + if (rc < 0) + { + int tmp; + tmp = oserror(); + close(fd); + setoserror(tmp); + return rc; + } + + return fd; +} + + +//--------------------------------------------------------------------------- +// Closes the connection to the port. +// +void CloseCOM(void) +{ + FlushCOM(); + close(fd); +} + + +//-------------------------------------------------------------------------- +// Set the baud rate on the com port. The possible baud rates for +// 'new_baud' are: +// +// PARMSET_9600 0x00 +// PARMSET_19200 0x02 +// PARMSET_57600 0x04 +// PARMSET_115200 0x06 +// +void SetBaudCOM(int new_baud) +{ + struct termios t; + int rc; + speed_t baud = B0; + + // read the attribute structure + rc = tcgetattr(fd, &t); + if (rc < 0) + { + close(fd); + return; + } + + // convert parameter to linux baud rate + switch(new_baud) + { + case PARMSET_9600: + baud = B9600; + break; + case PARMSET_19200: + baud = B19200; + break; + case PARMSET_57600: +#ifdef B57600 + baud = B57600; + break; +#else +#define ERR_MSG_57600 "SetBaudCOM: no support for 57600 baud, sorry!" + write(2, ERR_MSG_57600, strlen(ERR_MSG_57600)); + exit(1); +#endif + case PARMSET_115200: +#ifdef B115200 + baud = B115200; + break; +#else +#define ERR_MSG_115200 "SetBaudCOM: no support for 115200 baud, sorry!" + write(2, ERR_MSG_115200, strlen(ERR_MSG_115200)); + exit(1); +#endif + } + + // set baud in structure + cfsetospeed(&t, baud); + cfsetispeed(&t, baud); + + // change baud on port + rc = tcsetattr(fd, TCSAFLUSH, &t); + if (rc < 0) + close(fd); +} + + +//-------------------------------------------------------------------------- +// Get the current millisecond tick count. Does not have to represent +// an actual time, it just needs to be an incrementing timer. +// +long msGettick(void) +{ + struct timezone tmzone; + struct timeval tmval; + long ms; + + gettimeofday(&tmval,&tmzone); + ms = (tmval.tv_sec & 0xFFFF) * 1000 + tmval.tv_usec / 1000; + return ms; +} + diff --git a/src/pmdas/roomtemp/mlan/mlan.h b/src/pmdas/roomtemp/mlan/mlan.h new file mode 100644 index 0000000..4ca742b --- /dev/null +++ b/src/pmdas/roomtemp/mlan/mlan.h @@ -0,0 +1,94 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// MLan.H - Include file for MicroLAN library +// +// Version: 1.03 +// History: 1.02 -> 1.03 Make sure uchar is not defined twice. +// + +// Typedefs +#ifndef UCHAR + #define UCHAR + typedef unsigned char uchar; +#endif +#if 0 +// not needed most places - kenmcd +typedef unsigned short ushort; +typedef unsigned long ulong; +#endif + +// general defines +#define WRITE_FUNCTION 1 +#define READ_FUNCTION 0 + +// error codes +#define READ_ERROR -1 +#define INVALID_DIR -2 +#define NO_FILE -3 +#define WRITE_ERROR -4 +#define WRONG_TYPE -5 +#define FILE_TOO_BIG -6 + +// Misc +#define FALSE 0 +#define TRUE 1 + +// mode bit flags +#define MODE_NORMAL 0x00 +#define MODE_OVERDRIVE 0x01 +#define MODE_STRONG5 0x02 +#define MODE_PROGRAM 0x04 +#define MODE_BREAK 0x08 + +// product families +#define TEMP_FAMILY 0x10 +#define SWITCH_FAMILY 0x12 +#define COUNT_FAMILY 0x1D +#define DIR_FAMILY 0x01 + +// externs +extern uchar DOWCRC; + +// debugging +extern int MLanDebug; + +// function prototypes +extern int Aquire1WireNet(char *, char *); +extern void Release1WireNet(char *); +extern int MLanAccess(void); +extern int MLanBlock(int DoReset, uchar *TransferBuffer, int TransferLen); +extern void MLanFamilySearchSetup(int SearchFamily); +extern int MLanLevel(int NewLevel); +extern int MLanNext(int DoReset, int OnlyAlarmingDevices); +extern void MLanSerialNum(uchar *SerialNumBuf, int DoRead); +extern int MLanTouchByte(int sendbyte); +extern int MLanVerify(int OnlyAlarmingDevices); +extern uchar dowcrc(uchar x); +extern void msDelay(int len); +extern int DS2480ChangeBaud(uchar newbaud); +extern void SetBaudCOM(int new_baud); + diff --git a/src/pmdas/roomtemp/mlan/mlanllu.c b/src/pmdas/roomtemp/mlan/mlanllu.c new file mode 100644 index 0000000..91b23f0 --- /dev/null +++ b/src/pmdas/roomtemp/mlan/mlanllu.c @@ -0,0 +1,499 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// MLanLLU.C - Link Layer MicroLAN functions using the DS2480 (U) +// serial interface chip. +// +// Version: 1.03 +// +// History: 1.00 -> 1.01 DS2480 version number now ignored in +// MLanTouchReset. +// 1.02 -> 1.03 Removed caps in #includes for Linux capatibility +// Removed #include +// Add #include "mlan.h" to define TRUE,FALSE + +#include "ds2480.h" +#include "mlan.h" + +// external COM functions required +extern void FlushCOM(void); +extern int WriteCOM(int, uchar *); +extern int ReadCOM(int, uchar *); +//extern void SetBaudCOM(int); + +// external DS2480 utility function +extern int DS2480Detect(void); +//extern int DS2480ChangeBaud(int); + +// local functions +int MLanTouchReset(void); +int MLanTouchBit(int sendbit); +int MLanTouchByte(int sendbyte); +int MLanWriteByte(int sendbyte); +int MLanReadByte(void); +int MLanSpeed(int); +int MLanLevel(int); +int MLanProgramPulse(void); + +// external globals +extern int UMode; // current DS2480 command or data mode state +extern int UBaud; // current DS2480 baud rate +extern int USpeed; // current DS2480 MicroLAN communication speed +extern int ULevel; // current DS2480 MicroLAN level + +// local varable flag, true if program voltage available +static int ProgramAvailable=FALSE; + + +//-------------------------------------------------------------------------- +// Reset all of the devices on the MicroLAN and return the result. +// +// Returns: TRUE(1): presense pulse(s) detected, device(s) reset +// FALSE(0): no presense pulses detected +// +// WARNING: This routine will not function correctly on some +// Alarm reset types of the DS1994/DS1427/DS2404 with +// Rev 1 and 2 of the DS2480. +// +int MLanTouchReset(void) +{ + uchar readbuffer[10],sendpacket[10]; + int sendlen=0; + + // make sure normal level + MLanLevel(MODE_NORMAL); + + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + + // construct the command + sendpacket[sendlen++] = (uchar)(CMD_COMM | FUNCTSEL_RESET | USpeed); + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the 1 byte response + if (ReadCOM(1,readbuffer) == 1) + { + // make sure this byte looks like a reset byte + if (((readbuffer[0] & RB_RESET_MASK) == RB_PRESENCE) || + ((readbuffer[0] & RB_RESET_MASK) == RB_ALARMPRESENCE)) + { + // check if programming voltage available + ProgramAvailable = ((readbuffer[0] & 0x20) == 0x20); + return TRUE; + } + else + return FALSE; + } + } + + // an error occured so re-sync with DS2480 + DS2480Detect(); + + return FALSE; +} + + +//-------------------------------------------------------------------------- +// Send 1 bit of communication to the MicroLAN and return the +// result 1 bit read from the MicroLAN. The parameter 'sendbit' +// least significant bit is used and the least significant bit +// of the result is the return bit. +// +// 'sendbit' - the least significant bit is the bit to send +// +// Returns: 0: 0 bit read from sendbit +// 1: 1 bit read from sendbit +// +int MLanTouchBit(int sendbit) +{ + uchar readbuffer[10],sendpacket[10]; + int sendlen=0; + + // make sure normal level + MLanLevel(MODE_NORMAL); + + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + + // construct the command + sendpacket[sendlen] = (sendbit != 0) ? BITPOL_ONE : BITPOL_ZERO; + sendpacket[sendlen++] |= CMD_COMM | FUNCTSEL_BIT | USpeed; + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the response + if (ReadCOM(1,readbuffer) == 1) + { + // interpret the response + if (((readbuffer[0] & 0xE0) == 0x80) && + ((readbuffer[0] & RB_BIT_MASK) == RB_BIT_ONE)) + return 1; + else + return 0; + } + } + + // an error occured so re-sync with DS2480 + DS2480Detect(); + + return 0; +} + + +//-------------------------------------------------------------------------- +// Send 8 bits of communication to the MicroLAN and verify that the +// 8 bits read from the MicroLAN is the same (write operation). +// The parameter 'sendbyte' least significant 8 bits are used. +// +// 'sendbyte' - 8 bits to send (least significant byte) +// +// Returns: TRUE: bytes written and echo was the same +// FALSE: echo was not the same +// +int MLanWriteByte(int sendbyte) +{ + return (MLanTouchByte(sendbyte) == sendbyte) ? TRUE : FALSE; +} + + +//-------------------------------------------------------------------------- +// Send 8 bits of read communication to the MicroLAN and and return the +// result 8 bits read from the MicroLAN. +// +// Returns: 8 bytes read from MicroLAN +// +int MLanReadByte(void) +{ + return MLanTouchByte(0xFF); +} + + +//-------------------------------------------------------------------------- +// Send 8 bits of communication to the MicroLAN and return the +// result 8 bits read from the MicroLAN. The parameter 'sendbyte' +// least significant 8 bits are used and the least significant 8 bits +// of the result is the return byte. +// +// 'sendbyte' - 8 bits to send (least significant byte) +// +// Returns: 8 bytes read from sendbyte +// +int MLanTouchByte(int sendbyte) +{ + uchar readbuffer[10],sendpacket[10]; + int sendlen=0; + + // make sure normal level + MLanLevel(MODE_NORMAL); + + // check if correct mode + if (UMode != MODSEL_DATA) + { + UMode = MODSEL_DATA; + sendpacket[sendlen++] = MODE_DATA; + } + + // add the byte to send + sendpacket[sendlen++] = (uchar)sendbyte; + + // check for duplication of data that looks like COMMAND mode + if (sendbyte == MODE_COMMAND) + sendpacket[sendlen++] = (uchar)sendbyte; + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the 1 byte response + if (ReadCOM(1,readbuffer) == 1) + { + // return the response + return (int)readbuffer[0]; + } + } + + // an error occured so re-sync with DS2480 + DS2480Detect(); + + return 0; +} + + +//-------------------------------------------------------------------------- +// Set the MicroLAN communucation speed. +// +// 'NewSpeed' - new speed defined as +// MODE_NORMAL 0x00 +// MODE_OVERDRIVE 0x01 +// +// Returns: current MicroLAN speed +// +int MLanSpeed(int NewSpeed) +{ + uchar sendpacket[5]; + short sendlen=0; + int rt = FALSE; + + // check if change from current mode + if (((NewSpeed == MODE_OVERDRIVE) && + (USpeed != SPEEDSEL_OD)) || + ((NewSpeed == MODE_NORMAL) && + (USpeed != SPEEDSEL_FLEX))) + { + if (NewSpeed == MODE_OVERDRIVE) + { + // if overdrive then switch to 115200 baud + if (DS2480ChangeBaud(PARMSET_57600) == PARMSET_57600) + { + USpeed = SPEEDSEL_OD; + rt = TRUE; + } + } + else if (NewSpeed == MODE_NORMAL) + { + // else normal so set to 9600 baud + if (DS2480ChangeBaud(PARMSET_9600) == PARMSET_9600) + { + USpeed = SPEEDSEL_FLEX; + rt = TRUE; + } + } + + // if baud rate is set correctly then change DS2480 speed + if (rt) + { + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + + // proceed to set the DS2480 communication speed + sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_SEARCHOFF | USpeed; + + // send the packet + if (!WriteCOM(sendlen,sendpacket)) + { + rt = FALSE; + // lost communication with DS2480 then reset + DS2480Detect(); + } + } + } + + // return the current speed + return (USpeed == SPEEDSEL_OD) ? MODE_OVERDRIVE : MODE_NORMAL; +} + + +//-------------------------------------------------------------------------- +// Set the MicroLAN line level. The values for NewLevel are +// as follows: +// +// 'NewLevel' - new level defined as +// MODE_NORMAL 0x00 +// MODE_STRONG5 0x02 +// MODE_PROGRAM 0x04 +// MODE_BREAK 0x08 (not supported) +// +// Returns: current MicroLAN level +// +int MLanLevel(int NewLevel) +{ + uchar sendpacket[10],readbuffer[10]; + short sendlen=0; + short rt=FALSE; + + // check if need to change level + if (NewLevel != ULevel) + { + // check if just putting back to normal + if (NewLevel == MODE_NORMAL) + { + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + + // stop pulse command + sendpacket[sendlen++] = MODE_STOP_PULSE; + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the 1 byte response + if (ReadCOM(1,readbuffer) == 1) + { + // check response byte + if ((readbuffer[0] & 0xE0) == 0xE0) + { + rt = TRUE; + ULevel = MODE_NORMAL; + } + } + } + } + // set new level + else + { + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + + // strong 5 volts + if (NewLevel == MODE_STRONG5) + { + // set the SPUD time value + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_5VPULSE | PARMSET_infinite; + // add the command to begin the pulse + sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_CHMOD | SPEEDSEL_PULSE | BITPOL_5V; + } + // 12 volts + else if (NewLevel == MODE_PROGRAM) + { + // check if programming voltage available + if (!ProgramAvailable) + return MODE_NORMAL; + + // set the PPD time value + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_12VPULSE | PARMSET_infinite; + // add the command to begin the pulse + sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_CHMOD | SPEEDSEL_PULSE | BITPOL_12V; + } + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the 1 byte response from setting time limit + if (ReadCOM(1,readbuffer) == 1) + { + // check response byte + if ((readbuffer[0] & 0x81) == 0) + { + ULevel = NewLevel; + rt = TRUE; + } + } + } + } + + // if lost communication with DS2480 then reset + if (rt != TRUE) + DS2480Detect(); + } + + // return the current level + return ULevel; +} + + +//-------------------------------------------------------------------------- +// This procedure creates a fixed 480 microseconds 12 volt pulse +// on the MicroLAN for programming EPROM iButtons. +// +// Returns: TRUE successful +// FALSE program voltage not available +// +int MLanProgramPulse(void) +{ + uchar sendpacket[10],readbuffer[10]; + short sendlen=0; + + // check if programming voltage available + if (!ProgramAvailable) + return FALSE; + + // make sure normal level + MLanLevel(MODE_NORMAL); + + // check if correct mode + if (UMode != MODSEL_COMMAND) + { + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + } + + // set the SPUD time value + sendpacket[sendlen++] = CMD_CONFIG | PARMSEL_12VPULSE | PARMSET_512us; + + // pulse command + sendpacket[sendlen++] = CMD_COMM | FUNCTSEL_CHMOD | BITPOL_12V | SPEEDSEL_PULSE; + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the 2 byte response + if (ReadCOM(2,readbuffer) == 2) + { + // check response byte + if (((readbuffer[0] | CMD_CONFIG) == + (CMD_CONFIG | PARMSEL_12VPULSE | PARMSET_512us)) && + ((readbuffer[1] & 0xFC) == + (0xFC & (CMD_COMM | FUNCTSEL_CHMOD | BITPOL_12V | SPEEDSEL_PULSE)))) + return TRUE; + } + } + + // an error occured so re-sync with DS2480 + DS2480Detect(); + + return FALSE; +} + diff --git a/src/pmdas/roomtemp/mlan/mlannetu.c b/src/pmdas/roomtemp/mlan/mlannetu.c new file mode 100644 index 0000000..88ddbbd --- /dev/null +++ b/src/pmdas/roomtemp/mlan/mlannetu.c @@ -0,0 +1,599 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// MLanNetU.C - Network functions for MicroLAN 1-Wire devices +// using the DS2480 (U) serial interface chip. +// +// Version: 1.03 +// +// 1.02 -> 1.03 Removed caps in #includes for Linux capatibility +// + +#include "ds2480.h" +#include "mlan.h" + +// external MicroLAN functions required +extern int MLanTouchReset(void); +extern int MLanTouchBit(int); +extern int MLanWriteByte(int sendbyte); +extern int MLanReadByte(void); +extern int MLanSpeed(int); +extern int MLanLevel(int); +extern int MLanBlock(int, uchar *, int); + +// external COM functions required +extern void FlushCOM(void); +extern int WriteCOM(int, uchar *); +extern int ReadCOM(int, uchar *); + +// external DS2480 utility function +extern int DS2480Detect(void); + +// exportable functions +int MLanFirst(int,int); +int MLanNext(int,int); +void MLanSerialNum(uchar *, int); +void MLanFamilySearchSetup(int); +void MLanSkipFamily(void); +int MLanAccess(void); +int MLanVerify(int); +int MLanOverdriveAccess(void); + +// local functions +int bitacc(int, int, int, uchar *); +uchar dowcrc(uchar); + +// global variables for this module to hold search state information +static int LastDiscrepancy; +static int LastFamilyDiscrepancy; +static int LastDevice; +uchar DOWCRC; +uchar SerialNum[8]; + +// external globals +extern int UMode; +extern int UBaud; +extern int USpeed; + +//-------------------------------------------------------------------------- +// The 'MLanFirst' finds the first device on the MicroLAN This function +// contains one parameter 'OnlyAlarmingDevices'. When +// 'OnlyAlarmingDevices' is TRUE (1) the find alarm command 0xEC is +// sent instead of the normal search command 0xF0. +// Using the find alarm command 0xEC will limit the search to only +// 1-Wire devices that are in an 'alarm' state. +// +// 'DoReset' - TRUE (1) perform reset before search, FALSE (0) do not +// perform reset before search. +// 'OnlyAlarmDevices' - TRUE (1) the find alarm command 0xEC is +// sent instead of the normal search command 0xF0 +// +// Returns: TRUE (1) : when a 1-Wire device was found and it's +// Serial Number placed in the global SerialNum +// FALSE (0): There are no devices on the MicroLAN. +// +int MLanFirst(int DoReset, int OnlyAlarmingDevices) +{ + // reset the search state + LastDiscrepancy = 0; + LastDevice = FALSE; + LastFamilyDiscrepancy = 0; + + return MLanNext(DoReset, OnlyAlarmingDevices); +} + +//-------------------------------------------------------------------------- +// The 'MLanNext' function does a general search. This function +// continues from the previos search state. The search state +// can be reset by using the 'MLanFirst' function. +// This function contains one parameter 'OnlyAlarmingDevices'. +// When 'OnlyAlarmingDevices' is TRUE (1) the find alarm command +// 0xEC is sent instead of the normal search command 0xF0. +// Using the find alarm command 0xEC will limit the search to only +// 1-Wire devices that are in an 'alarm' state. +// +// 'DoReset' - TRUE (1) perform reset before search, FALSE (0) do not +// perform reset before search. +// 'OnlyAlarmDevices' - TRUE (1) the find alarm command 0xEC is +// sent instead of the normal search command 0xF0 +// +// Returns: TRUE (1) : when a 1-Wire device was found and it's +// Serial Number placed in the global SerialNum +// FALSE (0): when no new device was found. Either the +// last search was the last device or there +// are no devices on the MicroLAN. +// +int MLanNext(int DoReset, int OnlyAlarmingDevices) +{ + int i,TempLastDescrepancy,pos; + uchar TempSerialNum[8]; + uchar readbuffer[20],sendpacket[40]; + int sendlen=0; + + // if the last call was the last one + if (LastDevice) + { + // reset the search + LastDiscrepancy = 0; + LastDevice = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + + // check if reset first is requested + if (DoReset) + { + // reset the 1-wire + // if there are no parts on 1-wire, return FALSE + if (!MLanTouchReset()) + { + // reset the search + LastDiscrepancy = 0; + LastFamilyDiscrepancy = 0; + return FALSE; + } + } + + // build the command stream + // call a function that may add the change mode command to the buff + // check if correct mode + if (UMode != MODSEL_DATA) + { + UMode = MODSEL_DATA; + sendpacket[sendlen++] = MODE_DATA; + } + + // search command + if (OnlyAlarmingDevices) + sendpacket[sendlen++] = 0xEC; // issue the alarming search command + else + sendpacket[sendlen++] = 0xF0; // issue the search command + + // change back to command mode + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + + // search mode on + sendpacket[sendlen++] = (uchar)(CMD_COMM | FUNCTSEL_SEARCHON | USpeed); + + // change back to data mode + UMode = MODSEL_DATA; + sendpacket[sendlen++] = MODE_DATA; + + // set the temp Last Descrep to none + TempLastDescrepancy = 0xFF; + + // add the 16 bytes of the search + pos = sendlen; + for (i = 0; i < 16; i++) + sendpacket[sendlen++] = 0; + + // only modify bits if not the first search + if (LastDiscrepancy != 0xFF) + { + // set the bits in the added buffer + for (i = 0; i < 64; i++) + { + // before last discrepancy + if (i < (LastDiscrepancy - 1)) + bitacc(WRITE_FUNCTION, + bitacc(READ_FUNCTION,0,i,&SerialNum[0]), + (short)(i * 2 + 1), + &sendpacket[pos]); + // at last discrepancy + else if (i == (LastDiscrepancy - 1)) + bitacc(WRITE_FUNCTION,1, + (short)(i * 2 + 1), + &sendpacket[pos]); + // after last discrepancy so leave zeros + } + } + + // change back to command mode + UMode = MODSEL_COMMAND; + sendpacket[sendlen++] = MODE_COMMAND; + + // search OFF + sendpacket[sendlen++] = (uchar)(CMD_COMM | FUNCTSEL_SEARCHOFF | USpeed); + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the 1 byte response + if (ReadCOM(17,readbuffer) == 17) + { + // interpret the bit stream + for (i = 0; i < 64; i++) + { + // get the SerialNum bit + bitacc(WRITE_FUNCTION, + bitacc(READ_FUNCTION,0,(short)(i * 2 + 1),&readbuffer[1]), + i, + &TempSerialNum[0]); + // check LastDiscrepancy + if ((bitacc(READ_FUNCTION,0,(short)(i * 2),&readbuffer[1]) == 1) && + (bitacc(READ_FUNCTION,0,(short)(i * 2 + 1),&readbuffer[1]) == 0)) + { + TempLastDescrepancy = i + 1; + // check LastFamilyDiscrepancy + if (i < 8) + LastFamilyDiscrepancy = i + 1; + } + } + + // do dowcrc + DOWCRC = 0; + for (i = 0; i < 8; i++) + dowcrc(TempSerialNum[i]); + + // check results + if ((DOWCRC != 0) || (LastDiscrepancy == 63) || (TempSerialNum[0] == 0)) + { + // error during search + // reset the search + LastDiscrepancy = 0; + LastDevice = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + // successful search + else + { + // check for lastone + if ((TempLastDescrepancy == LastDiscrepancy) || (TempLastDescrepancy == 0xFF)) + LastDevice = TRUE; + + // copy the SerialNum to the buffer + for (i = 0; i < 8; i++) + SerialNum[i] = TempSerialNum[i]; + + // set the count + LastDiscrepancy = TempLastDescrepancy; + return TRUE; + } + } + } + + // an error occured so re-sync with DS2480 + DS2480Detect(); + + // reset the search + LastDiscrepancy = 0; + LastDevice = FALSE; + LastFamilyDiscrepancy = 0; + + return FALSE; +} + + +//-------------------------------------------------------------------------- +// The 'MLanSerialNum' function either reads or sets the SerialNum buffer +// that is used in the search functions 'MLanFirst' and 'MLanNext'. +// This function contains two parameters, 'SerialNumBuf' is a pointer +// to a buffer provided by the caller. 'SerialNumBuf' should point to +// an array of 8 unsigned chars. The second parameter is a flag called +// 'DoRead' that is TRUE (1) if the operation is to read and FALSE +// (0) if the operation is to set the internal SerialNum buffer from +// the data in the provided buffer. +// +// 'SerialNumBuf' - buffer to that contains the serial number to set +// when DoRead = FALSE (0) and buffer to get the serial +// number when DoRead = TRUE (1). +// 'DoRead' - flag to indicate reading (1) or setting (0) the current +// serial number. +// +void MLanSerialNum(uchar *SerialNumBuf, int DoRead) +{ + int i; + + // read the internal buffer and place in 'SerialNumBuf' + if (DoRead) + { + for (i = 0; i < 8; i++) + SerialNumBuf[i] = SerialNum[i]; + } + // set the internal buffer from the data in 'SerialNumBuf' + else + { + for (i = 0; i < 8; i++) + SerialNum[i] = SerialNumBuf[i]; + } +} + + +//-------------------------------------------------------------------------- +// Setup the search algorithm to find a certain family of devices +// the next time a search function is called 'MLanNext'. +// +// 'SearchFamily' - family code type to set the search algorithm to find +// next. +// +void MLanFamilySearchSetup(int SearchFamily) +{ + int i; + + // set the search state to find SearchFamily type devices + SerialNum[0] = (uchar)SearchFamily; + for (i = 1; i < 8; i++) + SerialNum[i] = 0; + LastDiscrepancy = 64; + LastDevice = FALSE; +} + + +//-------------------------------------------------------------------------- +// Set the current search state to skip the current family code. +// +void MLanSkipFamily(void) +{ + // set the Last discrepancy to last family discrepancy + LastDiscrepancy = LastFamilyDiscrepancy; + + // check for end of list + if (LastDiscrepancy == 0) + LastDevice = TRUE; +} + + +//-------------------------------------------------------------------------- +// The 'MLanAccess' function resets the 1-Wire and sends a MATCH Serial +// Number command followed by the current SerialNum code. After this +// function is complete the 1-Wire device is ready to accept device-specific +// commands. +// +// Returns: TRUE (1) : reset indicates present and device is ready +// for commands. +// FALSE (0): reset does not indicate presence or echos 'writes' +// are not correct. +// +int MLanAccess(void) +{ + uchar TranBuf[9]; + int i; + + // reset the 1-wire + if (MLanTouchReset()) + { + // create a buffer to use with block function + // match Serial Number command 0x55 + TranBuf[0] = 0x55; + // Serial Number + for (i = 1; i < 9; i++) + TranBuf[i] = SerialNum[i-1]; + + // send/recieve the transfer buffer + if (MLanBlock(FALSE,TranBuf,9)) + { + // verify that the echo of the writes was correct + for (i = 1; i < 9; i++) + if (TranBuf[i] != SerialNum[i-1]) + return FALSE; + if (TranBuf[0] != 0x55) + return FALSE; + else + return TRUE; + } + } + + // reset or match echo failed + return FALSE; +} + + +//---------------------------------------------------------------------- +// The function 'MLanVerify' verifies that the current device +// is in contact with the MicroLAN. +// Using the find alarm command 0xEC will verify that the device +// is in contact with the MicroLAN and is in an 'alarm' state. +// +// 'OnlyAlarmingDevices' - TRUE (1) the find alarm command 0xEC +// is sent instead of the normal search +// command 0xF0. +// +// Returns: TRUE (1) : when the 1-Wire device was verified +// to be on the MicroLAN +// with OnlyAlarmingDevices == FALSE +// or verified to be on the MicroLAN +// AND in an alarm state when +// OnlyAlarmingDevices == TRUE. +// FALSE (0): the 1-Wire device was not on the +// MicroLAN or if OnlyAlarmingDevices +// == TRUE, the device may be on the +// MicroLAN but in a non-alarm state. +// +int MLanVerify(int OnlyAlarmingDevices) +{ + int i,TranCnt=0,goodbits=0,cnt=0,s,tst; + uchar TranBuf[50]; + + // construct the search rom + if (OnlyAlarmingDevices) + TranBuf[TranCnt++] = 0xEC; // issue the alarming search command + else + TranBuf[TranCnt++] = 0xF0; // issue the search command + // set all bits at first + for (i = 1; i <= 24; i++) + TranBuf[TranCnt++] = 0xFF; + // now set or clear apropriate bits for search + for (i = 0; i < 64; i++) + bitacc(WRITE_FUNCTION,bitacc(READ_FUNCTION,0,i,&SerialNum[0]),(int)((i+1)*3-1),&TranBuf[1]); + + // send/recieve the transfer buffer + if (MLanBlock(TRUE,TranBuf,TranCnt)) + { + // check results to see if it was a success + for (i = 0; i < 192; i += 3) + { + tst = (bitacc(READ_FUNCTION,0,i,&TranBuf[1]) << 1) | + bitacc(READ_FUNCTION,0,(int)(i+1),&TranBuf[1]); + + s = bitacc(READ_FUNCTION,0,cnt++,&SerialNum[0]); + + if (tst == 0x03) // no device on line + { + goodbits = 0; // number of good bits set to zero + break; // quit + } + + if (((s == 0x01) && (tst == 0x02)) || + ((s == 0x00) && (tst == 0x01)) ) // correct bit + goodbits++; // count as a good bit + } + + // check too see if there were enough good bits to be successful + if (goodbits >= 8) + return TRUE; + } + + // block fail or device not present + return FALSE; +} + + +//---------------------------------------------------------------------- +// Perform a overdrive MATCH command to select the 1-Wire device with +// the address in the ID data register. +// +// Returns: TRUE: If the device is present on the MicroLAN and +// can do overdrive then the device is selected. +// FALSE: Device is not present or not capable of overdrive. +// +// *Note: This function could be converted to send DS2480 +// commands in one packet. +// +int MLanOverdriveAccess(void) +{ + uchar TranBuf[8]; + int i, EchoBad = FALSE; + + // make sure normal level + MLanLevel(MODE_NORMAL); + + // force to normal communication speed + MLanSpeed(MODE_NORMAL); + + // call the MicroLAN reset function + if (MLanTouchReset()) + { + // send the match command 0x69 + if (MLanWriteByte(0x69)) + { + // switch to overdrive communication speed + MLanSpeed(MODE_OVERDRIVE); + + // create a buffer to use with block function + // Serial Number + for (i = 0; i < 8; i++) + TranBuf[i] = SerialNum[i]; + + // send/recieve the transfer buffer + if (MLanBlock(FALSE,TranBuf,8)) + { + // verify that the echo of the writes was correct + for (i = 0; i < 8; i++) + if (TranBuf[i] != SerialNum[i]) + EchoBad = TRUE; + // if echo ok then success + if (!EchoBad) + return TRUE; + } + } + } + + // failure, force back to normal communication speed + MLanSpeed(MODE_NORMAL); + + return FALSE; +} + + +//-------------------------------------------------------------------------- +// Update the Dallas Semiconductor One Wire CRC (DOWCRC) from the global +// variable DOWCRC and the argument. +// +// 'x' - data byte to calculate the 8 bit crc from +// +// Returns: the updated DOWCRC. +// +uchar dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; + +uchar dowcrc(uchar x) +{ + DOWCRC = dscrc_table[DOWCRC ^ x]; + return DOWCRC; +} + + +//-------------------------------------------------------------------------- +// Bit utility to read and write a bit in the buffer 'buf'. +// +// 'op' - operation (1) to set and (0) to read +// 'state' - set (1) or clear (0) if operation is write (1) +// 'loc' - bit number location to read or write +// 'buf' - pointer to array of bytes that contains the bit +// to read or write +// +// Returns: 1 if operation is set (1) +// 0/1 state of bit number 'loc' if operation is reading +// +int bitacc(int op, int state, int loc, uchar *buf) +{ + int nbyt,nbit; + + nbyt = (loc / 8); + nbit = loc - (nbyt * 8); + + if (op == WRITE_FUNCTION) + { + if (state) + buf[nbyt] |= (0x01 << nbit); + else + buf[nbyt] &= ~(0x01 << nbit); + + return 1; + } + else + return ((buf[nbyt] >> nbit) & 0x01); +} diff --git a/src/pmdas/roomtemp/mlan/mlansesu.c b/src/pmdas/roomtemp/mlan/mlansesu.c new file mode 100644 index 0000000..b46c349 --- /dev/null +++ b/src/pmdas/roomtemp/mlan/mlansesu.c @@ -0,0 +1,102 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// MLanSesU.C - Aquire and release a Session on the 1-Wire Net. +// +// Version: 1.03 +// + +#include "pmapi.h" +#include "mlan.h" + +// external function prototypes +extern int OpenCOM(char *); +extern void CloseCOM(void); +extern int DS2480Detect(void); + +// local function prototypes +int Aquire1WireNet(char *, char *); +void Release1WireNet(char *); + +// keep port name for later message when closing +char portname[128]; + +// debugging +int MLanDebug = 0; + +//--------------------------------------------------------------------------- +// Attempt to aquire a 1-Wire net using a com port and a DS2480 based +// adapter. +// +// 'port_zstr' - zero terminated port name. For this platform +// use format COMX where X is the port number. +// 'return_msg' - zero terminated return message. +// +// Returns: TRUE - success, COM port opened +// +int Aquire1WireNet(char *port_zstr, char *return_msg) +{ + int cnt=0; + portname[0] = 0; + + // attempt to open the communications port + if (OpenCOM(port_zstr) >= 0) + cnt += sprintf(&return_msg[cnt],"%s opened\n",port_zstr); + else + { + cnt += sprintf(&return_msg[cnt],"Could not open port %s: %s," + " aborting.\nClosing port %s.\n",port_zstr,osstrerror(),port_zstr); + return FALSE; + } + + // detect DS2480 + if (DS2480Detect()) + cnt += sprintf(&return_msg[cnt],"DS2480-based adapter detected\n"); + else + { + cnt += sprintf(&return_msg[cnt],"DS2480-based adapter not detected, aborting program\n"); + cnt += sprintf(&return_msg[cnt],"Closing port %s.\n",port_zstr); + CloseCOM(); + return FALSE; + } + + // success + sprintf(portname,"%s",port_zstr); + return TRUE; +} + + +//--------------------------------------------------------------------------- +// Release the previously aquired a 1-Wire net. +// +// 'return_msg' - zero terminated return message. +// +void Release1WireNet(char *return_msg) +{ + // close the communications port + sprintf(return_msg,"Closing port %s.\n",portname); + CloseCOM(); +} diff --git a/src/pmdas/roomtemp/mlan/mlantrnu.c b/src/pmdas/roomtemp/mlan/mlantrnu.c new file mode 100644 index 0000000..5a0b376 --- /dev/null +++ b/src/pmdas/roomtemp/mlan/mlantrnu.c @@ -0,0 +1,579 @@ +//--------------------------------------------------------------------------- +// Copyright (C) 1999 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//--------------------------------------------------------------------------- +// +// MLanTranU.C - Transport functions for MicroLAN 1-Wire devices +// using the DS2480 (U) serial interface chip. +// +// Version: 1.03 +// +// 1.02 -> 1.03 Removed caps in #includes for Linux capatibility +// + +#include "ds2480.h" +#include "mlan.h" + +// external low-level functions required +extern int MLanTouchReset(void); +extern int MLanWriteByte(int); +extern int MLanReadByte(void); +extern int MLanProgramPulse(void); + +// external network-level functions required +extern int MLanAccess(); + +// external COM functions required +extern void FlushCOM(void); +extern int WriteCOM(int, uchar *); +extern int ReadCOM(int, uchar *); + +// other external functions +extern int DS2480Detect(void); +extern uchar dowcrc(uchar); + +// external globals +extern int UMode; +extern int UBaud; +extern int USpeed; +extern uchar SerialNum[8]; +extern uchar DOWCRC; + +// local exportable functions +int MLanBlock(int, uchar *, int); +int MLanReadPacketStd(int, int, uchar *); +int MLanWritePacketStd(int, uchar *, int, int, int); +int MLanProgramByte(int, int, int, int, int); + +// local functions +static int Write_Scratchpad(uchar *, int, int); +static int Copy_Scratchpad(int, int); +unsigned short crc16(int); + +// global variable +unsigned short CRC16; + + +//-------------------------------------------------------------------------- +// The 'MLanBlock' transfers a block of data to and from the +// MicroLAN with an optional reset at the begining of communication. +// The result is returned in the same buffer. +// +// 'DoReset' - cause a MLanTouchReset to occure at the begining of +// communication TRUE(1) or not FALSE(0) +// 'TransferBuffer' - pointer to a block of unsigned +// chars of length 'TranferLength' that will be sent +// to the MicroLAN +// 'TranferLength' - length in bytes to transfer + +// Supported devices: all +// +// Returns: TRUE (1) : The optional reset returned a valid +// presence (DoReset == TRUE) or there +// was no reset required. +// FALSE (0): The reset did not return a valid prsence +// (DoReset == TRUE). +// +// The maximum TransferLength is 64 +// +int MLanBlock(int DoReset, uchar *TransferBuffer, int TransferLen) +{ + uchar sendpacket[150]; + int sendlen=0,i; + + // check for a block too big + if (TransferLen > 64) + return FALSE; + + // check if need to do a MLanTouchReset first + if (DoReset) + { + if (!MLanTouchReset()) + return FALSE; + } + + // construct the packet to send to the DS2480 + // check if correct mode + if (UMode != MODSEL_DATA) + { + UMode = MODSEL_DATA; + sendpacket[sendlen++] = MODE_DATA; + } + + // add the bytes to send + for (i = 0; i < TransferLen; i++) + { + sendpacket[sendlen++] = TransferBuffer[i]; + + // check for duplication of data that looks like COMMAND mode + if (TransferBuffer[i] == MODE_COMMAND) + sendpacket[sendlen++] = TransferBuffer[i]; + } + + // flush the buffers + FlushCOM(); + + // send the packet + if (WriteCOM(sendlen,sendpacket)) + { + // read back the response + if (ReadCOM(TransferLen,TransferBuffer) == TransferLen) + return TRUE; + } + + // an error occured so re-sync with DS2480 + DS2480Detect(); + + return FALSE; +} + + +//-------------------------------------------------------------------------- +// Read a Universal Data Packet from a standard NVRAM iButton +// and return it in the provided buffer. The page that the +// packet resides on is 'StartPage'. Note that this function is limited +// to single page packets. The buffer 'ReadBuffer' must be at least +// 29 bytes long. +// +// The Universal Data Packet always start on page boundaries but +// can end anywhere. The length is the number of data bytes not +// including the length byte and the CRC16 bytes. There is one +// length byte. The CRC16 is first initialized to the starting +// page number. This provides a check to verify the page that +// was intended is being read. The CRC16 is then calculated over +// the length and data bytes. The CRC16 is then inverted and stored +// low byte first followed by the high byte. +// +// Supported devices: DS1992, DS1993, DS1994, DS1995, DS1996, DS1982, +// DS1985, DS1986, DS2407, and DS1971. +// +// 'DoAccess' - flag to indicate if an 'MLanAccess' should be +// peformed at the begining of the read. This may +// be FALSE (0) if the previous call was to read the +// previous page (StartPage-1). +// 'StartPage' - page number to start the read from +// 'ReadBuffer' - pointer to a location to store the data read +// +// Returns: >=0 success, number of data bytes in the buffer +// -1 failed to read a valid UDP +// +// +int MLanReadPacketStd(int DoAccess, int StartPage, uchar *ReadBuffer) +{ + int i,length,TranCnt=0,HeadLen=0; + uchar TranBuf[50]; + + // check if access header is done + // (only use if in sequention read with one access at begining) + if (DoAccess) + { + // match command + TranBuf[TranCnt++] = 0x55; + for (i = 0; i < 8; i++) + TranBuf[TranCnt++] = SerialNum[i]; + // read memory command + TranBuf[TranCnt++] = 0xF0; + // write the target address + TranBuf[TranCnt++] = ((StartPage << 5) & 0xFF); + TranBuf[TranCnt++] = (StartPage >> 3); + // check for DS1982 exception (redirection byte) + if (SerialNum[0] == 0x09) + TranBuf[TranCnt++] = 0xFF; + // record the header length + HeadLen = TranCnt; + } + // read the entire page length byte + for (i = 0; i < 32; i++) + TranBuf[TranCnt++] = 0xFF; + + // send/recieve the transfer buffer + if (MLanBlock(DoAccess,TranBuf,TranCnt)) + { + // seed crc with page number + CRC16 = StartPage; + + // attempt to read UDP from TranBuf + length = TranBuf[HeadLen]; + crc16(length); + + // verify length is not too large + if (length <= 29) + { + // loop to read packet including CRC + for (i = 0; i < length; i++) + { + ReadBuffer[i] = TranBuf[i+1+HeadLen]; + crc16(ReadBuffer[i]); + } + + // read and compute the CRC16 + crc16(TranBuf[i+1+HeadLen]); + crc16(TranBuf[i+2+HeadLen]); + + // verify the CRC16 is correct + if (CRC16 == 0xB001) + return length; // return number of byte in record + } + } + + // failed block or incorrect CRC + return -1; +} + + +//-------------------------------------------------------------------------- +// Write a Universal Data Packet onto a standard NVRAM 1-Wire device +// on page 'StartPage'. This function is limited to UDPs that +// fit on one page. The data to write is provided as a buffer +// 'WriteBuffer' with a length 'WriteLength'. +// +// The Universal Data Packet always start on page boundaries but +// can end anywhere. The length is the number of data bytes not +// including the length byte and the CRC16 bytes. There is one +// length byte. The CRC16 is first initialized to the starting +// page number. This provides a check to verify the page that +// was intended is being read. The CRC16 is then calculated over +// the length and data bytes. The CRC16 is then inverted and stored +// low byte first followed by the high byte. +// +// Supported devices: DeviceEPROM=0 +// DS1992, DS1993, DS1994, DS1995, DS1996 +// DeviceEPROM=1, EPROMCRCType=0(CRC8) +// DS1982 +// DeviceEPROM=1, EPROMCRCType=1(CRC16) +// DS1985, DS1986, DS2407 +// +// 'StartPage' - page number to write packet to +// 'WriteBuffer' - pointer to buffer containing data to write +// 'WriteLength' - number of data byte in WriteBuffer +// 'DeviceEPROM' - flag set if device is an EPROM (1 EPROM, 0 NVRAM) +// 'EPROMCRCType' - if DeviceEPROM=1 then indicates CRC type +// (0 CRC8, 1 CRC16) +// +// Returns: TRUE(1) success, packet written +// FALSE(0) failure to write, contact lost or device locked +// +// +int MLanWritePacketStd(int StartPage, uchar *WriteBuffer, + int WriteLength, int DeviceEPROM, int EPROMCRCType) +{ + uchar construct_buffer[32]; + int i,buffer_cnt=0,start_address,do_access; + + // check to see if data too long to fit on device + if (WriteLength > 29) + return FALSE; + + // seed crc with page number + CRC16 = StartPage; + + // set length byte + construct_buffer[buffer_cnt++] = (uchar)(WriteLength); + crc16(WriteLength); + + // fill in the data to write + for (i = 0; i < WriteLength; i++) + { + crc16(WriteBuffer[i]); + construct_buffer[buffer_cnt++] = WriteBuffer[i]; + } + + // add the crc + construct_buffer[buffer_cnt++] = (uchar)(~(CRC16 & 0xFF)); + construct_buffer[buffer_cnt++] = (uchar)(~((CRC16 & 0xFF00) >> 8)); + + // check if not EPROM + if (!DeviceEPROM) + { + // write the page + if (!Write_Scratchpad(construct_buffer,StartPage,buffer_cnt)) + return FALSE; + + // copy the scratchpad + if (!Copy_Scratchpad(StartPage,buffer_cnt)) + return FALSE; + + // copy scratch pad was good then success + return TRUE; + } + // is EPROM + else + { + // calculate the start address + start_address = ((StartPage >> 3) << 8) | ((StartPage << 5) & 0xFF); + do_access = TRUE; + // loop to program each byte + for (i = 0; i < buffer_cnt; i++) + { + if (MLanProgramByte(construct_buffer[i], start_address + i, + 0x0F, EPROMCRCType, do_access) != construct_buffer[i]) + return FALSE; + do_access = FALSE; + } + return TRUE; + } +} + + +//-------------------------------------------------------------------------- +// Write a byte to an EPROM 1-Wire device. +// +// Supported devices: CRCType=0(CRC8) +// DS1982 +// CRCType=1(CRC16) +// DS1985, DS1986, DS2407 +// +// 'WRByte' - byte to program +// 'Addr' - address of byte to program +// 'WriteCommand' - command used to write (0x0F reg mem, 0x55 status) +// 'CRCType' - CRC used (0 CRC8, 1 CRC16) +// 'DoAccess' - Flag to access device for each byte +// (0 skip access, 1 do the access) +// WARNING, only use DoAccess=0 if programing the NEXT +// byte immediatly after the previous byte. +// +// Returns: >=0 success, this is the resulting byte from the program +// effort +// -1 error, device not connected or program pulse voltage +// not available +// +int MLanProgramByte(int WRByte, int Addr, int WriteCommand, + int CRCType, int DoAccess) +{ + // optionally access the device + if (DoAccess) + { + if (!MLanAccess()) + return -1; + + // send the write command + if (!MLanWriteByte(WriteCommand)) + return -1; + + // send the address + if (!MLanWriteByte(Addr & 0xFF)) + return -1; + if (!MLanWriteByte(Addr >> 8)) + return -1; + } + + // send the data to write + if (!MLanWriteByte(WRByte)) + return -1; + + // read the CRC + if (CRCType == 0) + { + // calculate CRC8 + if (DoAccess) + { + DOWCRC = 0; + dowcrc((uchar)WriteCommand); + dowcrc((uchar)(Addr & 0xFF)); + dowcrc((uchar)(Addr >> 8)); + } + else + DOWCRC = (uchar)(Addr & 0xFF); + + dowcrc((uchar)WRByte); + // read and calculate the read crc + dowcrc((uchar)MLanReadByte()); + // crc should now be 0x00 + if (DOWCRC != 0) + return -1; + } + else + { + // CRC16 + if (DoAccess) + { + CRC16 = 0; + crc16(WriteCommand); + crc16(Addr & 0xFF); + crc16(Addr >> 8); + } + else + CRC16 = Addr; + crc16(WRByte); + // read and calculate the read crc + crc16(MLanReadByte()); + crc16(MLanReadByte()); + // crc should now be 0xB001 + if (CRC16 != 0xB001) + return -1; + } + + // send the program pulse + if (!MLanProgramPulse()) + return -1; + + // read back and return the resulting byte + return MLanReadByte(); +} + + +//-------------------------------------------------------------------------- +// Write the scratchpad of a standard NVRam device such as the DS1992,3,4 +// and verify its contents. +// +// 'WriteBuffer' - pointer to buffer containing data to write +// 'StartPage' - page number to write packet to +// 'WriteLength' - number of data byte in WriteBuffer +// +// Returns: TRUE(1) success, the data was written and verified +// FALSE(0) failure, the data could not be written +// +// +int Write_Scratchpad(uchar *WriteBuffer, int StartPage, int WriteLength) +{ + int i,TranCnt=0; + uchar TranBuf[50]; + + // match command + TranBuf[TranCnt++] = 0x55; + for (i = 0; i < 8; i++) + TranBuf[TranCnt++] = SerialNum[i]; + // write scratchpad command + TranBuf[TranCnt++] = 0x0F; + // write the target address + TranBuf[TranCnt++] = ((StartPage << 5) & 0xFF); + TranBuf[TranCnt++] = (StartPage >> 3); + + // write packet bytes + for (i = 0; i < WriteLength; i++) + TranBuf[TranCnt++] = WriteBuffer[i]; + + // send/recieve the transfer buffer + if (MLanBlock(TRUE,TranBuf,TranCnt)) + { + // now attempt to read back to check + TranCnt = 0; + // match command + TranBuf[TranCnt++] = 0x55; + for (i = 0; i < 8; i++) + TranBuf[TranCnt++] = SerialNum[i]; + // read scratchpad command + TranBuf[TranCnt++] = 0xAA; + // read the target address, offset and data + for (i = 0; i < (WriteLength + 3); i++) + TranBuf[TranCnt++] = 0xFF; + + // send/recieve the transfer buffer + if (MLanBlock(TRUE,TranBuf,TranCnt)) + { + // check address and offset of scratchpad read + if ((TranBuf[10] != (int)((StartPage << 5) & 0xFF)) || + (TranBuf[11] != (int)(StartPage >> 3)) || + (TranBuf[12] != (int)(WriteLength - 1))) + return FALSE; + + // verify each data byte + for (i = 0; i < WriteLength; i++) + if (TranBuf[i+13] != WriteBuffer[i]) + return FALSE; + + // must have verified + return TRUE; + } + } + + // failed a block tranfer + return FALSE; +} + + +//-------------------------------------------------------------------------- +// Copy the contents of the scratchpad to its intended nv ram page. The +// page and length of the data is needed to build the authorization bytes +// to copy. +// +// 'StartPage' - page number to write packet to +// 'WriteLength' - number of data bytes that are being copied +// +// Returns: TRUE(1) success +// FALSE(0) failure +// +int Copy_Scratchpad(int StartPage, int WriteLength) +{ + int i,TranCnt=0; + uchar TranBuf[50]; + + // match command + TranBuf[TranCnt++] = 0x55; + for (i = 0; i < 8; i++) + TranBuf[TranCnt++] = SerialNum[i]; + // copy scratchpad command + TranBuf[TranCnt++] = 0x55; + // write the target address + TranBuf[TranCnt++] = ((StartPage << 5) & 0xFF); + TranBuf[TranCnt++] = (StartPage >> 3); + TranBuf[TranCnt++] = WriteLength - 1; + // read copy result + TranBuf[TranCnt++] = 0xFF; + + // send/recieve the transfer buffer + if (MLanBlock(TRUE,TranBuf,TranCnt)) + { + // check address and offset of scratchpad read + if ((TranBuf[10] != (int)((StartPage << 5) & 0xFF)) || + (TranBuf[11] != (int)(StartPage >> 3)) || + (TranBuf[12] != (int)(WriteLength - 1)) || + (TranBuf[13] & 0xF0)) + return FALSE; + else + return TRUE; + } + + // failed a block tranfer + return FALSE; +} + + +//-------------------------------------------------------------------------- +// Calculate a new CRC16 from the input data integer. Return the current +// CRC16 and also update the global variable CRC16. +// +// 'data' - data to perform a CRC16 on +// +// Returns: the current CRC16 +// +static short oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + +unsigned short crc16(int data) +{ + data = (data ^ (CRC16 & 0xff)) & 0xff; + CRC16 >>= 8; + + if (oddparity[data & 0xf] ^ oddparity[data >> 4]) + CRC16 ^= 0xc001; + + data <<= 6; + CRC16 ^= data; + data <<= 1; + CRC16 ^= data; + + return CRC16; +} + + diff --git a/src/pmdas/roomtemp/pmns b/src/pmdas/roomtemp/pmns new file mode 100644 index 0000000..7843f5c --- /dev/null +++ b/src/pmdas/roomtemp/pmns @@ -0,0 +1,24 @@ +/* + * Metrics for roomtemp PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +roomtemp { + celsius ROOMTEMP:0:0 + fahrenheit ROOMTEMP:0:1 +} diff --git a/src/pmdas/roomtemp/roomtemp.c b/src/pmdas/roomtemp/roomtemp.c new file mode 100644 index 0000000..510cf7c --- /dev/null +++ b/src/pmdas/roomtemp/roomtemp.c @@ -0,0 +1,211 @@ +/* + * Roomtemp PMDA + * + * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "dsread.h" + +/* + * Roomtemp PMDA + * + * This PMDA exports the temperature from one or more sensors built using + * the DS2480 and DS1280 chipsets and MicroLAN technology from Dallas + * Semiconductor Corporation. + */ + +/* + * Serial device + */ +static char *tty; + +/* + * list of instances + */ + +static pmdaInstid *device = NULL; + +/* + * list of instance domains + */ + +static pmdaIndom indomtab[] = { +#define DEVICE 0 + { DEVICE, 0, NULL }, +}; + +typedef struct { + unsigned char sn[8]; +} sn_t; + +sn_t *sntab = NULL; + +/* + * All metrics supported in this PMDA - one table entry for each. + * The 4th field specifies the serial number of the instance domain + * for the metric, and must be either PM_INDOM_NULL (denoting a + * metric that only ever has a single value), or the serial number + * of one of the instance domains declared in the instance domain table + * (i.e. in indomtab, above). + */ + +static pmdaMetric metrictab[] = { +/* roomtemp.celsius */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_FLOAT, DEVICE, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* roomtemp.fahrenheit */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_FLOAT, DEVICE, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +/* + * callback provided to pmdaFetch + */ +static int +roomtemp_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + char return_msg[128]; + int numval = 0; + + if (idp->cluster == 0) { + if (inst >= indomtab[DEVICE].it_numinst) + return PM_ERR_INST; + switch (idp->item) { + case 0: /* roomtemp.celsius */ + case 1: /* roomtemp.fahrenheit */ + if (!Aquire1WireNet(tty, return_msg)) { + fputs(return_msg, stderr); + exit(1); + } + if (ReadTemperature(sntab[inst].sn, &atom->f)) + numval = 1; + Release1WireNet(return_msg); + if (idp->item == 1) + /* convert to fahrenheit */ + atom->f = atom->f * 9 / 5 + 32; + break; + + default: + return PM_ERR_PMID; + } + } + else + return PM_ERR_PMID; + + return numval; +} + +/* + * Initialise the agent + */ +void +roomtemp_init(pmdaInterface *dp) +{ + int i; + char return_msg[128]; + unsigned char *p; + + if (dp->status != 0) + return; + + pmdaSetFetchCallBack(dp, roomtemp_fetchCallBack); + + if (!Aquire1WireNet(tty, return_msg)) { + fputs(return_msg, stderr); + exit(1); + } + for (i = 0; ; i++) { + if ((p = nextsensor()) == NULL) + break; + if ((sntab = (sn_t *)realloc(sntab, (i+1) * sizeof(sn_t))) == NULL) { + __pmNoMem("roomtemp_init: realloc sntab", (i+1) * sizeof(sn_t), PM_FATAL_ERR); + } + if ((device = (pmdaInstid *)realloc(device, (i+1) * sizeof(pmdaInstid))) == NULL) { + __pmNoMem("roomtemp_init: realloc device", (i+1) * sizeof(pmdaInstid), PM_FATAL_ERR); + } + if ((device[i].i_name = (char *)malloc(17)) == NULL) { + __pmNoMem("roomtemp_init: malloc name", 17, PM_FATAL_ERR); + } + memcpy(sntab[i].sn, p, 8); /* SN for later fetch */ + device[i].i_inst = i; /* internal name is ordinal number */ + /* external name is SN in hex */ + sprintf(device[i].i_name, "%02X%02X%02X%02X%02X%02X%02X%02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + fprintf(stderr, "Found temp sensor SN %s\n", device[i].i_name); + } + Release1WireNet(return_msg); + indomtab[DEVICE].it_numinst = i; + indomtab[DEVICE].it_set = device; + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); + +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options] tty ...\n\n", pmProgname); + fputs("Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + "\nExactly one of the following options may appear:\n" + " -i port expect PMCD to connect on given inet port (number or name)\n" + " -p expect PMCD to supply stdin/stdout (pipe)\n" + " -u socket expect PMCD to connect on given unix domain socket\n" + " -6 port expect PMCD to connect on given ipv6 port (number or name)\n", + stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int err = 0; + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + + snprintf(mypath, sizeof(mypath), "%s%c" "roomtemp" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, ROOMTEMP, + "roomtemp.log", mypath); + + if (pmdaGetOpt(argc, argv, "D:d:i:l:pu:6:?", &dispatch, &err) != EOF) + err++; + if (err) + usage(); + if (argc != optind+1) + usage(); + tty = argv[optind]; + + pmdaOpenLog(&dispatch); + roomtemp_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/roomtemp/root b/src/pmdas/roomtemp/root new file mode 100644 index 0000000..2035fb5 --- /dev/null +++ b/src/pmdas/roomtemp/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { roomtemp } + +#include "pmns" + diff --git a/src/pmdas/rpm/GNUmakefile b/src/pmdas/rpm/GNUmakefile new file mode 100644 index 0000000..29738bf --- /dev/null +++ b/src/pmdas/rpm/GNUmakefile @@ -0,0 +1,67 @@ +# +# Copyright (c) 2013, 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = rpm +DOMAIN = RPM +CMDTARGET = pmda$(IAM)$(EXECSUFFIX) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMDAINIT = $(IAM)_init + +HFILES = rpm.h timer.h +CFILES = rpm.c timer.c + +SCRIPTS = Install Remove +VERSION_SCRIPT = exports +LSRCFILES = Install Remove pmns root help +LDIRT = domain.h $(IAM).log $(VERSION_SCRIPT) + +LIB_FOR_RPM = -lrpm +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_RPM) $(LIB_FOR_PTHREADS) +LCFLAGS = $(INVISIBILITY) + +default: build-me + +include $(BUILDRULES) + +ifeq ($(HAVE_RPMLIB),1) +build-me: domain.h $(CMDTARGET) $(LIBTARGET) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 root pmns domain.h help $(PMDADIR) + $(INSTALL) -m 755 $(CMDTARGET) $(LIBTARGET) $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 migrate.conf $(PCP_VAR_DIR)/config/pmlogrewrite/rpm_migrate.conf +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +rpm.o: rpm.h +rpm.o timer.o: timer.h +rpm.o: $(VERSION_SCRIPT) diff --git a/src/pmdas/rpm/Install b/src/pmdas/rpm/Install new file mode 100644 index 0000000..8f11751 --- /dev/null +++ b/src/pmdas/rpm/Install @@ -0,0 +1,31 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the rpm PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=rpm +pmda_interface=5 +forced_restart=false + +dso_opt=true +pipe_opt=true +daemon_opt=true + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/rpm/Remove b/src/pmdas/rpm/Remove new file mode 100644 index 0000000..73e423f --- /dev/null +++ b/src/pmdas/rpm/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the rpm PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=rpm + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/rpm/help b/src/pmdas/rpm/help new file mode 100644 index 0000000..2a0320a --- /dev/null +++ b/src/pmdas/rpm/help @@ -0,0 +1,57 @@ +# +# Copyright (c) 2013-2014 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# rpm PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ rpm.arch package architecture +@ rpm.buildhost package build host +@ rpm.buildtime package buildtime +@ rpm.description package description +@ rpm.epoch package install epoch +@ rpm.group group of the package +@ rpm.installtime package install time +@ rpm.license package license +@ rpm.packager entity responsible for packaging +@ rpm.release release list of the package +@ rpm.size size of the package in bytes +@ rpm.sourcerpm package source rpm +@ rpm.summary summary of the package +@ rpm.url url of the package +@ rpm.vendor package vendor +@ rpm.version package version +@ rpm.name package name + +@ rpm.refresh.count Cumulative count of rpmdb scans performed +@ rpm.refresh.time.user Cumulative count of user mode scan time +@ rpm.refresh.time.sys Cumulative count of kernel mode scan time +@ rpm.refresh.time.elapsed Cumulative count of elapsed scan time +@ rpm.datasize Space allocated for pmdarpms data segment (K) +This metric returns the amount of memory in kilobytes allocated for the +data segment of pmdarpm. This is handy for tracking memory utilization. + +@ rpm.total.count Count of packages returned in last rpmdb scan +@ rpm.total.bytes Size of all packages from the last rpmdb scan diff --git a/src/pmdas/rpm/migrate.conf b/src/pmdas/rpm/migrate.conf new file mode 100644 index 0000000..1eb4086 --- /dev/null +++ b/src/pmdas/rpm/migrate.conf @@ -0,0 +1,8 @@ +# Copyright 2014 Red Hat. +# +# pmlogrewrite configuration for migrating archives containing old +# 32 bit rpm values to the 64 bit variants, matching up +# with changes in the metadata supplied by the PMDA. +# + +metric 123.1.10 { type -> U64 } diff --git a/src/pmdas/rpm/pmns b/src/pmdas/rpm/pmns new file mode 100644 index 0000000..84aa5b3 --- /dev/null +++ b/src/pmdas/rpm/pmns @@ -0,0 +1,38 @@ +rpm { + arch RPM:1:0 + buildhost RPM:1:1 + buildtime RPM:1:2 + description RPM:1:3 + epoch RPM:1:4 + group RPM:1:5 + installtime RPM:1:6 + license RPM:1:7 + packager RPM:1:8 + release RPM:1:9 + size RPM:1:10 + sourcerpm RPM:1:11 + summary RPM:1:12 + url RPM:1:13 + vendor RPM:1:14 + version RPM:1:15 + name RPM:1:16 + refresh + datasize RPM:0:4 + total +} + +rpm.refresh { + count RPM:0:0 + time +} + +rpm.refresh.time { + user RPM:0:1 + sys RPM:0:2 + elapsed RPM:0:3 +} + +rpm.total { + count RPM:2:0 + bytes RPM:2:1 +} diff --git a/src/pmdas/rpm/root b/src/pmdas/rpm/root new file mode 100644 index 0000000..fb869d5 --- /dev/null +++ b/src/pmdas/rpm/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { rpm } + +#include "pmns" + diff --git a/src/pmdas/rpm/rpm.c b/src/pmdas/rpm/rpm.c new file mode 100644 index 0000000..348d9c1 --- /dev/null +++ b/src/pmdas/rpm/rpm.c @@ -0,0 +1,704 @@ +/* + * RPM Package Manager PMDA + * + * Copyright (c) 2013-2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "domain.h" +#include "timer.h" +#include "rpm.h" + +static pmdaIndom indomtab[] = { + {RPM_INDOM, 0, NULL}, + {CACHE_INDOM, 1, NULL}, + {STRINGS_INDOM, 2, NULL}, +}; + +static pmdaMetric metrictab[] = { + /* PMDA internals metrics - timing, count of refreshes, memory */ + { NULL, { PMDA_PMID(0, REFRESH_COUNT_ID), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE)}}, + { NULL, { PMDA_PMID(0, REFRESH_TIME_USER_ID), PM_TYPE_DOUBLE, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0)}}, + { NULL, { PMDA_PMID(0, REFRESH_TIME_KERNEL_ID), PM_TYPE_DOUBLE, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0)}}, + { NULL, { PMDA_PMID(0, REFRESH_TIME_ELAPSED_ID), PM_TYPE_DOUBLE, + PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0)}}, + { NULL, { PMDA_PMID(0, DATASIZE_ID), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0)}}, + + /* rpm package metrics */ + { NULL, { PMDA_PMID(1, ARCH_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, BUILDHOST_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, BUILDTIME_ID), PM_TYPE_U32, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0)}}, + { NULL, { PMDA_PMID(1, DESCRIPTION_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, EPOCH_ID), PM_TYPE_U32, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, GROUP_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, INSTALLTIME_ID), PM_TYPE_U32, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0)}}, + { NULL, { PMDA_PMID(1, LICENSE_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, PACKAGER_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, RELEASE_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, SIZE_ID), PM_TYPE_U64, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, + { NULL, { PMDA_PMID(1, SOURCERPM_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, SUMMARY_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, URL_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, VENDOR_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, VERSION_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + { NULL, { PMDA_PMID(1, NAME_ID), PM_TYPE_STRING, + RPM_INDOM, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0)}}, + + /* cumulative rpm metrics - total package count, size */ + { NULL, { PMDA_PMID(2, TOTAL_COUNT_ID), PM_TYPE_U32, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE)}}, + { NULL, { PMDA_PMID(2, TOTAL_BYTES_ID), PM_TYPE_U64, + PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0)}}, +}; + +static pthread_t inotify_thread; /* runs all librpm queries, esp. when the rpmdb changes */ +static unsigned long long numrefresh; /* updated by background thread, protected by indom_mutex */ +static unsigned long long packagesize; /* sum of sizes of all packages */ +static unsigned long numpackages; /* total count for all packages */ + +static pthread_mutex_t indom_mutex; + +static int isDSO = 1; /* invoked as shlib or daemon */ +static char *username; +static char *dbpath = "/var/lib/rpm/Packages"; + +static pmInDom +INDOM(int serial) +{ + return indomtab[serial].it_indom; +} + +static char * +dict_lookup(int index) +{ + char *value; + pmInDom dict = INDOM(STRINGS_INDOM); + + if (pmdaCacheLookup(dict, index, &value, NULL) == PMDA_CACHE_ACTIVE) + return value; + return ""; +} + +static int +dict_insert(const char *string) +{ + pmInDom dict = INDOM(STRINGS_INDOM); + if (!string) + string = ""; + return pmdaCacheStore(dict, PMDA_CACHE_ADD, string, NULL); +} + +static int +rpm_fetch_pmda(int item, pmAtomValue *atom) +{ + int sts = PMDA_FETCH_STATIC; + unsigned long datasize; + + switch (item) { + case REFRESH_COUNT_ID: /* rpm.refresh.count */ + atom->ull = numrefresh; /* XXX: unlocked */ + break; + case REFRESH_TIME_USER_ID: /* rpm.refresh.time.user */ + atom->d = get_user_timer(); + break; + case REFRESH_TIME_KERNEL_ID: /* rpm.refresh.time.kernel */ + atom->d = get_kernel_timer(); + break; + case REFRESH_TIME_ELAPSED_ID: /* rpm.refresh.time.elapsed */ + atom->d = get_elapsed_timer(); + break; + case DATASIZE_ID: /* rpm.datasize */ + __pmProcessDataSize(&datasize); + atom->ul = datasize; + break; + default: + sts = PM_ERR_PMID; + break; + } + return sts; +} + +static int +rpm_fetch_package(int item, unsigned int inst, pmAtomValue *atom) +{ + package *p; + char *name; + int sts; + + sts = pmdaCacheLookup(INDOM(RPM_INDOM), inst, &name, (void **)&p); + if (sts < 0 || sts == PMDA_CACHE_INACTIVE) + return PM_ERR_INST; + + sts = PMDA_FETCH_STATIC; + switch (item) { + case ARCH_ID: + atom->cp = dict_lookup(p->values.arch); + break; + case BUILDHOST_ID: + atom->cp = dict_lookup(p->values.buildhost); + break; + case BUILDTIME_ID: + atom->ul = p->values.buildtime; + break; + case DESCRIPTION_ID: + atom->cp = dict_lookup(p->values.description); + break; + case EPOCH_ID: + atom->ul = p->values.epoch; + break; + case GROUP_ID: + atom->cp = dict_lookup(p->values.group); + break; + case INSTALLTIME_ID: + atom->ul = p->values.installtime; + break; + case LICENSE_ID: + atom->cp = dict_lookup(p->values.license); + break; + case PACKAGER_ID: + atom->cp = dict_lookup(p->values.packager); + break; + case RELEASE_ID: + atom->cp = dict_lookup(p->values.release); + break; + case SIZE_ID: + atom->ull = p->values.longsize; + break; + case SOURCERPM_ID: + atom->cp = dict_lookup(p->values.sourcerpm); + break; + case SUMMARY_ID: + atom->cp = dict_lookup(p->values.summary); + break; + case URL_ID: + atom->cp = dict_lookup(p->values.url); + break; + case VENDOR_ID: + atom->cp = dict_lookup(p->values.vendor); + break; + case VERSION_ID: + atom->cp = dict_lookup(p->values.version); + break; + case NAME_ID: + atom->cp = dict_lookup(p->values.name); + break; + default: + sts = PM_ERR_PMID; + break; + } + return sts; +} + +static int +rpm_fetch_totals(int item, pmAtomValue *atom) +{ + int sts = PMDA_FETCH_STATIC; + + switch (item) { + case TOTAL_COUNT_ID: /* rpm.total.count */ + atom->ul = numpackages; + break; + case TOTAL_BYTES_ID: /* rpm.total.bytes */ + atom->ull = packagesize; + break; + default: + sts = PM_ERR_PMID; + break; + } + return sts; +} + +static int +rpm_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *) &mdesc->m_desc.pmid; + int sts; + + pthread_mutex_lock(&indom_mutex); + switch (idp->cluster) { + case 0: + if (inst != PM_IN_NULL) + sts = PM_ERR_INST; + else + sts = rpm_fetch_pmda(idp->item, atom); + break; + case 1: + sts = rpm_fetch_package(idp->item, inst, atom); + break; + case 2: + sts = rpm_fetch_totals(idp->item, atom); + break; + default: + sts = PM_ERR_PMID; + break; + } + pthread_mutex_unlock(&indom_mutex); + return sts; +} + +/* + * Sync the active rpm package instances with the reference database + * maintained by the background threads. + */ +static void +rpm_indom_refresh(unsigned long long refresh) +{ + pmInDom rpmdb, cache; + package *p; + char *name; + int sts; + + rpmdb = INDOM(RPM_INDOM); + cache = INDOM(CACHE_INDOM); + + pmdaCacheOp(rpmdb, PMDA_CACHE_INACTIVE); + + pthread_mutex_lock(&indom_mutex); + for (pmdaCacheOp(cache, PMDA_CACHE_WALK_REWIND);;) { + if ((sts = pmdaCacheOp(cache, PMDA_CACHE_WALK_NEXT)) < 0) + break; + if ((pmdaCacheLookup(cache, sts, &name, (void **)&p) < 0) || !p) + continue; + if (p->refresh < refresh) + continue; + pmdaCacheStore(rpmdb, PMDA_CACHE_ADD, name, (void *)p); + } + pthread_mutex_unlock(&indom_mutex); +} + +/* + * Sync up with the (initial) indom loading thread + */ +static int +notready(pmdaExt *pmda) +{ + unsigned iterations = 0; + + __pmSendError(pmda->e_outfd, FROM_ANON, PM_ERR_PMDANOTREADY); + + /* + * We need to wait for at least the initial rpm_update_cache() + * cycle to have finished. We could use a pthread condition + * variable, except that those have timing constraints on + * wait-precede-signal that we cannot enforce. So we poll. + */ + while (1) { + unsigned long long refresh; + + pthread_mutex_lock(&indom_mutex); + refresh = numrefresh; + pthread_mutex_unlock(&indom_mutex); + + if (refresh > 0) + break; + + if (iterations++ > 30) { /* Complain every 30 seconds. */ + __pmNotifyErr(LOG_WARNING, "notready waited too long"); + iterations = 0; /* XXX: or exit? */ + } + sleep(1); + } + + return PM_ERR_PMDAREADY; +} + +/* + * Called once for each pmFetch(3) operation + */ +static int +rpm_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + unsigned long long refresh; + + pthread_mutex_lock(&indom_mutex); + refresh = numrefresh; + pthread_mutex_unlock(&indom_mutex); + + if (refresh == 0) + return notready(pmda); + rpm_indom_refresh(refresh); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * Called once for each pmGetInDom(3) operation + */ +static int +rpm_instance(pmInDom id, int i, char *name, __pmInResult **in, pmdaExt *pmda) +{ + unsigned long long refresh; + + pthread_mutex_lock(&indom_mutex); + refresh = numrefresh; + pthread_mutex_unlock(&indom_mutex); + + if (refresh == 0) + return notready(pmda); + rpm_indom_refresh(refresh); + return pmdaInstance(id, i, name, in, pmda); +} + +static const char * +rpm_extract_string(rpmtd td, Header h, int tag) +{ + headerGet(h, tag, td, HEADERGET_EXT | HEADERGET_MINMEM); + /* + * RPM_STRING_ARRAY_TYPE being the alternative, e.g. filenames + * (which we never expect to see, for the metrics we export). + */ + if (td->type == RPM_STRING_ARRAY_TYPE) + __pmNotifyErr(LOG_ERR, + "rpm_extract_string: unexpected string array: %d", tag); + + return rpmtdGetString(td); +} + +static __uint64_t +rpm_extract_value(rpmtd td, Header h, int tag) +{ + __uint64_t value; + + headerGet(h, tag, td, HEADERGET_EXT | HEADERGET_MINMEM); + switch (td->type) { + case RPM_INT8_TYPE: + value = ((char *)(td->data))[0]; + break; + case RPM_INT16_TYPE: + value = ((short *)(td->data))[0]; + break; + case RPM_INT32_TYPE: + value = ((int *)(td->data))[0]; + break; + case RPM_INT64_TYPE: + value = ((long long *)(td->data))[0]; + break; + default: + value = 0; + break; + } + return value; +} + +static void +rpm_extract_metadata(const char *name, rpmtd td, Header h, metadata *m) +{ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_INFO, "updating package %s metadata", name); + + m->name = dict_insert(rpm_extract_string(td, h, RPMTAG_NAME)); + m->arch = dict_insert(rpm_extract_string(td, h, RPMTAG_ARCH)); + m->buildhost = dict_insert(rpm_extract_string(td, h, RPMTAG_BUILDHOST)); + m->buildtime = rpm_extract_value(td, h, RPMTAG_BUILDTIME); + m->description = dict_insert(rpm_extract_string(td, h, RPMTAG_DESCRIPTION)); + m->epoch = rpm_extract_value(td, h, RPMTAG_EPOCH); + m->group = dict_insert(rpm_extract_string(td, h, RPMTAG_GROUP)); + m->installtime = rpm_extract_value(td, h, RPMTAG_INSTALLTIME); + m->license = dict_insert(rpm_extract_string(td, h, RPMTAG_LICENSE)); + m->packager = dict_insert(rpm_extract_string(td, h, RPMTAG_PACKAGER)); + m->release = dict_insert(rpm_extract_string(td, h, RPMTAG_RELEASE)); + m->longsize = rpm_extract_value(td, h, RPMTAG_LONGSIZE); + m->sourcerpm = dict_insert(rpm_extract_string(td, h, RPMTAG_SOURCERPM)); + m->summary = dict_insert(rpm_extract_string(td, h, RPMTAG_SUMMARY)); + m->url = dict_insert(rpm_extract_string(td, h, RPMTAG_URL)); + m->vendor = dict_insert(rpm_extract_string(td, h, RPMTAG_VENDOR)); + m->version = dict_insert(rpm_extract_string(td, h, RPMTAG_VERSION)); +} + +/* + * Refresh the RPM package names and values in the cache. + * This is to be only ever invoked from a single thread. + */ +void * +rpm_update_cache(void *ptr) +{ + rpmtd td; + rpmts ts; + Header h; + rpmdbMatchIterator mi; + unsigned long long refresh; + unsigned long long totalsize = 0; + unsigned long packages = 0; + static int rpmReadConfigFiles_p = 0; + + pthread_mutex_lock(&indom_mutex); + start_timing(); + refresh = numrefresh + 1; /* current iteration */ + pthread_mutex_unlock(&indom_mutex); + + /* + * It appears unnecessary to check the return value from these functions, + * since the only (?) thing that can fail is memory allocation, which + * rpmlib internally maps to an exit(1). + */ + td = rpmtdNew(); + ts = rpmtsCreate(); + + if (rpmReadConfigFiles_p == 0) { + int sts = rpmReadConfigFiles(NULL, NULL); + if (sts == -1) + __pmNotifyErr(LOG_WARNING, "rpm_update_cache: rpmReadConfigFiles failed: %d", sts); + rpmReadConfigFiles_p = 1; + } + + /* Iterate through the entire list of RPMs, extract names and values */ + mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0); + while ((h = rpmdbNextIterator(mi)) != NULL) { + headerGet(h, RPMTAG_NEVRA, td, HEADERGET_EXT | HEADERGET_MINMEM); + const char *name = rpmtdGetString(td); + metadata meta; + package *pp = NULL; + int sts, err = 0; + + /* extract an on-stack copy of the package metadata, may do I/O */ + rpm_extract_metadata(name, td, h, &meta); + + /* update cumulative counts */ + totalsize += meta.longsize; + packages++; + + /* we now have our data and cannot need more I/O; lock and load */ + pthread_mutex_lock(&indom_mutex); + sts = pmdaCacheLookupName(INDOM(CACHE_INDOM), name, NULL, (void **)&pp); + if (sts == PM_ERR_INST || (sts >= 0 && pp == NULL)) { + /* allocate space for new package entry for the cache */ + if ((pp = calloc(1, sizeof(package))) == NULL) + err = 1; + } else if (sts < 0) { + err = 1; + } + + if (!err) { + /* update values in cache entry for this package (locked) */ + pp->refresh = refresh; + memcpy(&pp->values, &meta, sizeof(metadata)); + pmdaCacheStore(INDOM(CACHE_INDOM), PMDA_CACHE_ADD, name, (void *)pp); + } else { + /* ensure the logfile isn't spammed over and over */ + static int cache_err = 0; + if (cache_err++ < 10) { + fprintf(stderr, "rpm_refresh_cache: " + "pmdaCacheLookupName(%s, %s, ... %p) failed: %s\n", + pmInDomStr(INDOM(CACHE_INDOM)), name, pp, pmErrStr(sts)); + } + } + pthread_mutex_unlock(&indom_mutex); + } + + rpmdbFreeIterator(mi); + rpmtsFree(ts); + + pthread_mutex_lock(&indom_mutex); + stop_timing(); + numrefresh = refresh; /* current iteration complete */ + packagesize = totalsize; + numpackages = packages; + pthread_mutex_unlock(&indom_mutex); + return NULL; +} + +/* + * Notice when the rpm database changes and reload the instances. + */ +void * +rpm_inotify(void *ptr) +{ + char buffer[EVENT_BUF_LEN]; /* space for lots of events */ + int fd; + int sts; + + /* Update it the first time. */ + rpm_update_cache(ptr); + + /* + * By this time, the global refresh counter should be >= 1, even + * if some rpm* or other api failure occurred. + */ + fd = inotify_init(); + if (fd < 0) { + __pmNotifyErr(LOG_ERR, "rpm_inotify: failed to create inotify fd"); + return NULL; + } + + sts = inotify_add_watch(fd, dbpath, IN_CLOSE_WRITE); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "rpm_inotify: failed to inotify-watch dbpath %s", dbpath); + close(fd); + return NULL; + } + + while (1) { + int read_count; + + /* Wait for changes in the rpm database */ + read_count = read(fd, buffer, EVENT_BUF_LEN); + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_INFO, "rpm_inotify: read_count=%d", read_count); + + /* + * No need to check the contents of the buffer; having + * received an event at all indicates need to refresh. + */ + if (read_count <= 0) { + __pmNotifyErr(LOG_WARNING, "rpm_inotify: read_count=%d", read_count); + continue; + } + + rpm_update_cache(ptr); + + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_INFO, "rpm_inotify: refresh done"); + } + + /* NOTREACHED */ + return NULL; +} + +/* + * Initialize the daemon/.so agent. + */ + +void +__PMDA_INIT_CALL +rpm_init(pmdaInterface * dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + char helppath[MAXPATHLEN]; + + snprintf(helppath, sizeof(helppath), "%s%c" "rpm" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_5, "rpm DSO", helppath); + } + else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.any.fetch = rpm_fetch; + dp->version.any.instance = rpm_instance; + pmdaSetFetchCallBack(dp, rpm_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab) / sizeof(indomtab[0]), + metrictab, sizeof(metrictab) / sizeof(metrictab[0])); + + pmdaCacheOp(INDOM(STRINGS_INDOM), PMDA_CACHE_STRINGS); + + pthread_mutex_init(&indom_mutex, NULL); + /* Monitor changes to the rpm database */ + pthread_create(&inotify_thread, NULL, rpm_inotify, NULL); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n\n", pmProgname); + fprintf(stderr, "Options:\n" + " -C parse the RPM database, and exit\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -r path path to directory containing RPM database (default %s)\n" + " -U username user account to run under (default \"pcp\")\n" + "\nExactly one of the following options may appear:\n" + " -i port expect PMCD to connect on given inet port (number or name)\n" + " -p expect PMCD to supply stdin/stdout (pipe)\n" + " -u socket expect PMCD to connect on given unix domain socket\n" + " -6 port expect PMCD to connect on given ipv6 port (number or name)\n", + dbpath); + exit(1); +} + +/* + * Set up the agent if running as a daemon. + */ + +int +main(int argc, char **argv) +{ + int c, err = 0; + int Cflag = 0, sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmProcessDataSize(NULL); + __pmGetUsername(&username); + + snprintf(helppath, sizeof(helppath), "%s%c" "rpm" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_5, pmProgname, RPM, + "rpm.log", helppath); + + while ((c = + pmdaGetOpt(argc, argv, "CD:d:i:l:pr:u:6:U:?", &dispatch, + &err)) != EOF) { + switch (c) { + case 'C': + Cflag++; + break; + case 'U': + username = optarg; + break; + case 'r': + dbpath = optarg; + break; + default: + err++; + } + } + if (err) + usage(); + + pmdaOpenLog(&dispatch); + rpm_init(&dispatch); + if (Cflag) { + rpm_update_cache(NULL); + exit(0); + } + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/rpm/rpm.h b/src/pmdas/rpm/rpm.h new file mode 100644 index 0000000..117f197 --- /dev/null +++ b/src/pmdas/rpm/rpm.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef RPM_H +#define RPM_H + +/* + * Instance domain handling + */ +enum { + RPM_INDOM = 0, /* active RPM packages */ + CACHE_INDOM = 1, /* pseudo-indom for refreshing */ + STRINGS_INDOM = 2, /* pseudo-indom for string sharing */ +}; + +/* + * Metrics describing internals of pmdarpm operation (Cluster 0) + */ +enum { + REFRESH_COUNT_ID = 0, + REFRESH_TIME_USER_ID = 1, + REFRESH_TIME_KERNEL_ID = 2, + REFRESH_TIME_ELAPSED_ID = 3, + DATASIZE_ID = 4, +}; + +/* + * List of metrics corresponding to rpm --querytags (Cluster 1) + */ +enum { + ARCH_ID = 0, + BUILDHOST_ID = 1, + BUILDTIME_ID = 2, + DESCRIPTION_ID = 3, + EPOCH_ID = 4, + GROUP_ID = 5, + INSTALLTIME_ID = 6, + LICENSE_ID = 7, + PACKAGER_ID = 8, + RELEASE_ID = 9, + SIZE_ID = 10, + SOURCERPM_ID = 11, + SUMMARY_ID = 12, + URL_ID = 13, + VENDOR_ID = 14, + VERSION_ID = 15, + NAME_ID = 16, +}; + +/* + * Metrics describing cumulative pmdarpm totals (Cluster 2) + */ +enum { + TOTAL_COUNT_ID = 0, + TOTAL_BYTES_ID = 1, +}; + +/* + * Package metadata stored for each installed RPM + * + * A "refresh" count is stored to indicate whether this entry + * is out of date with respect to the global "refresh" count. + * If its value is greater-than-or-equal-to a global refresh + * count, the entry is current - otherwise it is out-of-date + * and must not be reported in the active instance domain. + * + * Note that many of the structure entries (below) are string + * dictionary keys (int), allowing sharing of the memory used + * to hold the values. It also further reduces the footprint + * on 64 bit systems, instead of storing 64bit pointers. + */ + +typedef struct metadata { + int name; + int arch; + int buildhost; + int buildtime; + int description; + int epoch; + int group; + int installtime; + int license; + int packager; + int release; + __uint64_t longsize; + int sourcerpm; + int summary; + int url; + int vendor; + int version; +} metadata; + +typedef struct package { + __uint64_t refresh; + metadata values; +} package; + +#define EVENT_SIZE ( sizeof (struct inotify_event) ) +#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) + +#endif /* RPM_H */ diff --git a/src/pmdas/rpm/timer.c b/src/pmdas/rpm/timer.c new file mode 100644 index 0000000..89cd8aa --- /dev/null +++ b/src/pmdas/rpm/timer.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include + +static struct rusage start_rsrc, final_rsrc; +static struct timeval start_time, final_time; +static double user, kernel, elapsed; + +double get_user_timer() { return user; } +double get_kernel_timer() { return kernel; } +double get_elapsed_timer() { return elapsed; } + +void +start_timing(void) +{ + getrusage(RUSAGE_SELF, &start_rsrc); + gettimeofday(&start_time, NULL); +} + +void +stop_timing(void) +{ + gettimeofday(&final_time, NULL); + getrusage(RUSAGE_SELF, &final_rsrc); + + /* accumulate the totals as we go */ + user += __pmtimevalSub(&final_rsrc.ru_utime, &start_rsrc.ru_utime); + kernel += __pmtimevalSub(&final_rsrc.ru_stime, &start_rsrc.ru_stime); + elapsed += __pmtimevalSub(&final_time, &start_time); +} diff --git a/src/pmdas/rpm/timer.h b/src/pmdas/rpm/timer.h new file mode 100644 index 0000000..529ca33 --- /dev/null +++ b/src/pmdas/rpm/timer.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef TIMER_H +#define TIMER_H + +extern void start_timing(void); +extern void stop_timing(void); + +extern double get_user_timer(void); +extern double get_kernel_timer(void); +extern double get_elapsed_timer(void); + +#endif /* TIMER_H */ diff --git a/src/pmdas/rsyslog/GNUmakefile b/src/pmdas/rsyslog/GNUmakefile new file mode 100644 index 0000000..db3914a --- /dev/null +++ b/src/pmdas/rsyslog/GNUmakefile @@ -0,0 +1,48 @@ +#!gmake +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = rsyslog +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/rsyslog/Install b/src/pmdas/rsyslog/Install new file mode 100755 index 0000000..33d2ac8 --- /dev/null +++ b/src/pmdas/rsyslog/Install @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the rsyslog PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=rsyslog +perl_opt=true +daemon_opt=false +forced_restart=false + +statsfile="$PCP_LOG_DIR/rsyslog/stats" +statsdir=`dirname "$statsfile"` + +if ! test -d "$statsdir"; then + echo "Creating rsyslog statistics file directory: $statsdir" + mkdir "$statsdir" + [ $? -eq 0 ] || exit 1 +fi + +if ! test -p "$statsfile"; then + echo "Creating rsyslog statistics file: $statsfile" + mkfifo "$statsfile" + [ $? -eq 0 ] || exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/rsyslog/Remove b/src/pmdas/rsyslog/Remove new file mode 100755 index 0000000..d460669 --- /dev/null +++ b/src/pmdas/rsyslog/Remove @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the rsyslog PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=rsyslog + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/rsyslog/pmdarsyslog.pl b/src/pmdas/rsyslog/pmdarsyslog.pl new file mode 100644 index 0000000..e3972f9 --- /dev/null +++ b/src/pmdas/rsyslog/pmdarsyslog.pl @@ -0,0 +1,249 @@ +# +# Copyright (c) 2012-2013 Red Hat. +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +my $pmda = PCP::PMDA->new('rsyslog', 107); +my $statsfile = pmda_config('PCP_LOG_DIR') . '/rsyslog/stats'; +my ($es_connfail, $es_submits, $es_failed, $es_success) = (0,0,0,0); +my ($ux_submitted, $ux_discarded, $ux_ratelimiters) = (0,0,0); +my ($interval, $lasttime) = (0,0); + +my $queue_indom = 0; +my @queue_insts = (); +use vars qw(%queue_ids %queue_values); + +# .* rsyslogd-pstats: +# imuxsock: submitted=37 ratelimit.discarded=0 ratelimit.numratelimiters=22 +# elasticsearch: connfail=0 submits=0 failed=0 success=0 +# [main Q]: size=1 enqueued=1436 full=0 maxqsize=3 + +sub rsyslog_parser +{ + ( undef, $_ ) = @_; + + #$pmda->log("rsyslog_parser got line: $_"); + if (m|rsyslogd-pstats:|) { + my $timenow = time; + if ($lasttime != 0) { + if ($timenow > $lasttime) { + $interval = $timenow - $lasttime; + $lasttime = $timenow; + } + } else { + $lasttime = $timenow; + } + } + if (m|imuxsock: submitted=(\d+) ratelimit.discarded=(\d+) ratelimit.numratelimiters=(\d+)|) { + ($ux_submitted, $ux_discarded, $ux_ratelimiters) = ($1,$2,$3); + } + elsif (m|elasticsearch: connfail=(\d+) submits=(\d+) failed=(\d+) success=(\d+)|) { + ($es_connfail, $es_submits, $es_failed, $es_success) = ($1,$2,$3,$4); + } + elsif (m|stats: (.+): size=(\d+) enqueued=(\d+) full=(\d+) maxqsize=(\d+)|) { + my ($qname, $qid) = ($1, undef); + + if (!defined($queue_ids{$qname})) { + $qid = @queue_insts / 2; + $queue_ids{$qname} = $qid; + push @queue_insts, ($qid, $qname); + $pmda->replace_indom($queue_indom, \@queue_insts); + } + $queue_values{$qname} = [ $2, $3, $4, $5 ]; + } +} + +sub rsyslog_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + + #$pmda->log("rsyslog_fetch_callback for PMID: $cluster.$item ($inst)"); + + return (PM_ERR_AGAIN,0) unless ($interval != 0); + + if ($cluster == 0) { + return (PM_ERR_INST, 0) unless ($inst == PM_IN_NULL); + if ($item == 0) { return ($interval, 1); } + if ($item == 1) { return ($ux_submitted, 1); } + if ($item == 2) { return ($ux_discarded, 1); } + if ($item == 3) { return ($ux_ratelimiters, 1); } + if ($item == 8) { return ($es_connfail, 1); } + if ($item == 9) { return ($es_submits, 1); } + if ($item == 10){ return ($es_failed, 1); } + if ($item == 11){ return ($es_success, 1); } + } + elsif ($cluster == 1) { # queues + return (PM_ERR_INST, 0) unless ($inst != PM_IN_NULL); + return (PM_ERR_INST, 0) unless ($inst <= @queue_insts); + my $qname = $queue_insts[$inst * 2 + 1]; + my $qvref = $queue_values{$qname}; + my @qvals; + + return (PM_ERR_INST, 0) unless defined ($qvref); + @qvals = @$qvref; + + if ($item == 0) { return ($qvals[0], 1); } + if ($item == 1) { return ($qvals[1], 1); } + if ($item == 2) { return ($qvals[2], 1); } + if ($item == 3) { return ($qvals[3], 1); } + } + return (PM_ERR_PMID, 0); +} + +die "Cannot find a valid rsyslog statistics named pipe\n" unless -p $statsfile; + +$pmda->connect_pmcd; + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_SEC,0), 'rsyslog.interval', + 'Time interval observed between samples', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.imuxsock.submitted', + 'Cumulative count of unix domain socket input messages queued', + "Cumulative count of messages successfully queued to the rsyslog\n" . + "main message queueing core that arrived on unix domain sockets."); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.imuxsock.discarded', + 'Count of unix domain socket messages discarded due to rate limiting', + "Cumulative count of messages that are were discarded due to their\n" . + "priority being at or below rate-limit-severity and their sending\n" . + "process being deemed to be sending messages too quickly (refer to\n" . + "parameters ratelimitburst, ratelimitinterval and ratelimitseverity"); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,0,0,0,0), 'rsyslog.imuxsock.numratelimiters', + 'Count of messages received that could be subject to rate limiting', + "Cumulative count of messages that rsyslog received and performed a\n" . + "credentials (PID) lookup for subsequent rate limiting decisions.\n" . + "The message would have to be at rate-limit-severity or lower, with\n" . + "rate limiting enabled, in order for this count to be incremented."); +$pmda->add_metric(pmda_pmid(0,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.elasticsearch.connfail', + 'Count of failed connections while attempting to send events', ''); +$pmda->add_metric(pmda_pmid(0,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.elasticsearch.submits', + 'Count of valid submissions of events to elasticsearch indexer', ''); +$pmda->add_metric(pmda_pmid(0,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.elasticsearch.failed', + 'Count of failed attempts to send events to elasticsearch', + 'This count is often a good indicator of malformed JSON messages'); +$pmda->add_metric(pmda_pmid(0,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.elasticsearch.success', + 'Count of successfully acknowledged events from elasticsearch', ''); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U64, $queue_indom, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'rsyslog.queues.size', + 'Current queue depth for each rsyslog queue', + "As messages arrive they are enqueued to the main message queue\n" . + "(for example) -this counter is incremented for each such message."); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U64, $queue_indom, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.queues.enqueued', + 'Cumulative count of nessages enqueued to individual queues', + "As messages arrive they are added to the main message processing\n" . + "queue, either individually or in batches in the case of messages\n" . + "arriving on the network."); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_U64, $queue_indom, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.queues.full', + 'Cumulative count of message arrivals with a full queue', + "When messages are enqueued, a check is first made to ensure the\n" . + "queue is not full. If it is, this counter is incremented. The\n" . + "full-queue-handling logic will wait for a configurable time for\n" . + "the queue congestion to ease, failing which the message will be\n" . + "discarded. Worth keeping an eye on this metric, as it indicates\n" . + "rsyslog is not able to process messages quickly enough given the\n" . + "current arrival rate."); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_U64, $queue_indom, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'rsyslog.queues.maxsize', + 'Maximum depth reached by an individual queue', + "When messages arrive (for example) they are enqueued to the main\n" . + "message queue - if the queue length on arrival is now greater than\n" . + "ever before observed, we set this value to the current queue size"); + +$pmda->add_indom($queue_indom, \@queue_insts, + 'Instance domain exporting each rsyslog queue', ''); + +$pmda->add_tail($statsfile, \&rsyslog_parser, 0); +$pmda->set_fetch_callback(\&rsyslog_fetch_callback); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdarsyslog - rsyslog (reliable and extended syslog) PMDA + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +exports metric values from the rsyslogd(8) server. +Further details about rsyslog can be found at http://www.rsyslog.com/. + +=head1 INSTALLATION + +If you want access to the names and values for the rsyslog performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/rsyslog + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/rsyslog + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +In order to use this agent, rsyslog stats gathering must be enabled. +This is done by adding the lines: + + $ModLoad impstats + $PStatsInterval 5 # log every 5 seconds + syslog.info |/var/log/pcp/rsyslog/stats + +to your rsyslog.conf(5) configuration file after installing the PMDA. +Take care to ensure the syslog.info messages do not get logged in any +other file, as this could unexpectedly fill your filesystem. Syntax +useful for this is syslog.!=info for explicitly excluding these. + +=head1 FILES + +=over + +=item /var/log/pcp/rsyslog/stats + +named pipe containing statistics exported from rsyslog, +usually created by the PMDA Install script. + +=item $PCP_PMDAS_DIR/rsyslog/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/rsyslog/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/rsyslog.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), rsyslog.conf(5), rsyslogd(8). diff --git a/src/pmdas/samba/GNUmakefile b/src/pmdas/samba/GNUmakefile new file mode 100644 index 0000000..e5602e2 --- /dev/null +++ b/src/pmdas/samba/GNUmakefile @@ -0,0 +1,52 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = samba +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) +else +install: +endif + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/samba/Install b/src/pmdas/samba/Install new file mode 100755 index 0000000..8a1aa7f --- /dev/null +++ b/src/pmdas/samba/Install @@ -0,0 +1,43 @@ +#! /bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Samba PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=samba +perl_opt=true +daemon_opt=false + +if ! test -x /usr/sbin/smbd; then + echo "Samba \"smbd\" daemon is not installed" && exit 1 +fi +/usr/sbin/smbd -b | egrep 'WITH_PROFILE|HAVE_PROFILE' >/dev/null +if test $? -ne 0; then + echo "Samba \"smbd\" not built with profiling support" && exit 1 +fi + +if ! test -x /usr/bin/smbcontrol; then + echo "Samba \"smbcontrol\" tool is not installed" && exit 1 +fi +/usr/bin/smbcontrol smbd profile on +if test $? -ne 0; then + echo "Samba \"smbcontrol\" failed to enable profiling" && exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/samba/Remove b/src/pmdas/samba/Remove new file mode 100755 index 0000000..9e92afe --- /dev/null +++ b/src/pmdas/samba/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Samba PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=samba + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/samba/pmdasamba.pl b/src/pmdas/samba/pmdasamba.pl new file mode 100644 index 0000000..f10d7e1 --- /dev/null +++ b/src/pmdas/samba/pmdasamba.pl @@ -0,0 +1,198 @@ +# +# Copyright (c) 2012-2013 Red Hat. +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +use vars qw( $pmda %metrics ); + +# +# This is the main workhorse routine, both value extraction and +# namespace population is under-pinned by this. The approach we +# use here is to extract profile output, construct a hash (keyed +# by metric ID), containing name and value pairs (array refs). +# +sub samba_fetch +{ + my $item = 0; + my $cluster = 0; + my $prefix = ''; + my $generated_cluster = 20; # start well above hard-coded ones + + # work around smbstatus / libpopt adverse reaction to these variables + delete $ENV{'POSIXLY_CORRECT'}; + delete $ENV{'POSIX_ME_HARDER'}; + + my $smbstats = "smbstatus --profile"; + open(STATS, "$smbstats |") || + $pmda->err("pmdasamba failed to open $smbstats pipe: $!"); + + while () { + if (m/^\*\*\*\*\s+(\w+[^*]*)\**$/) { + my $heading = $1; + $heading =~ s/ +$//g; + $item = 0; + if ($heading eq 'System Calls') { + $cluster = 1; $prefix = 'syscalls'; + } elsif ($heading eq 'Stat Cache') { + $cluster = 2; $prefix = 'statcache'; + } elsif ($heading eq 'Write Cache') { + $cluster = 3; $prefix = 'writecache'; + } elsif ($heading eq 'SMB Calls') { + $cluster = 4; $prefix = 'smb'; + } elsif ($heading eq 'Pathworks Calls') { + $cluster = 5; $prefix = 'pathworks'; + } elsif ($heading eq 'Trans2 Calls') { + $cluster = 6; $prefix = 'trans2'; + } elsif ($heading eq 'NT Transact Calls') { + $cluster = 7; $prefix = 'NTtransact'; + } elsif ($heading eq 'ACL Calls') { + $cluster = 8; $prefix = 'acl'; + } elsif ($heading eq 'NMBD Calls') { + $cluster = 9; $prefix = 'nmb'; + } else { + # samba 4.1 renames several clusters of statistics. + # Let's generate cluster names instead of hard-coding them. + $cluster = $generated_cluster++; + $prefix = $heading; + $prefix =~ s/ /_/g; + $prefix =~ tr/A-Z/a-z/; + } + # $pmda->log("metric cluster: $cluster = $prefix"); + } + # we've found a real name/value pair, work out PMID and hash it + elsif (m/^([\[\]\w]+):\s+(\d+)$/) { + my @metric = ( $1, $2 ); + my $pmid; + + $metric[0] =~ tr/\[\]/_/d; + + if ($cluster == 0) { + $metric[0] = "samba.$metric[0]"; + } else { + $metric[0] = "samba.$prefix.$metric[0]"; + } + $pmid = pmda_pmid($cluster,$item++); + $metrics{$pmid} = \@metric; + # $pmda->log("metric: $metric[0], ID = $pmid, value = $metric[1]"); + } + else { + $pmda->log("pmdasamba failed to parse line $_"); + } + } + close STATS; +} + +sub samba_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $pmid = pmda_pmid($cluster, $item); + my $value; + +# $pmda->log("samba_fetch_callback $metric_name $cluster:$item ($inst)\n"); + + if ($inst != PM_IN_NULL) { return (PM_ERR_INST, 0); } + + # hash lookup based on PMID, value is $metrics{$pmid}[1] + $value = $metrics{$pmid}; + if (!defined($value)) { return (PM_ERR_APPVERSION, 0); } + return ($value->[1], 1); +} + +$pmda = PCP::PMDA->new('samba', 76); + +samba_fetch(); # extract names and values into %metrics, keyed on PMIDs + +# hash iterate, keys are PMIDs, names and values are in @metrics{$pmid}. +foreach my $pmid (sort(keys %metrics)) { + my $name = $metrics{$pmid}[0]; + if ($name eq 'samba.writecache.num_write_caches' || + $name eq 'samba.writecache.allocated_caches') { + $pmda->add_metric($pmid, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), $name, '', ''); + } elsif ($name =~ /_time$/) { + $pmda->add_metric($pmid, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,1,0,0,PM_TIME_USEC,0), $name, '', ''); + } else { + $pmda->add_metric($pmid, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), $name, '', ''); + } + # $pmda->log("pmdasamba added metric $name\n"); +} +# close STATS; + +$pmda->set_fetch(\&samba_fetch); +$pmda->set_fetch_callback(\&samba_fetch_callback); +# NB: needs to run as root, as smb usually does +$pmda->run; + +=pod + +=head1 NAME + +pmdasamba - Samba performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from Samba, a Windows SMB/CIFS server for UNIX. + +In order for values to be made available by this PMDA, Samba must have +been built with profiling support (WITH_PROFILE in "smbd -b" output). +This PMDA dynamically enumerates much of its metric hierarchy, based on +the contents of "smbstatus --profile". + +When the agent is installed (see below), the Install script will attempt +to enable Samba statistics gathering, using "smbcontrol --profile". + +=head1 INSTALLATION + +If you want access to the names and values for the samba performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/samba + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/samba + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/samba/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/samba/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/samba.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1), smbd(1) and samba(7). diff --git a/src/pmdas/sample/GNUmakefile b/src/pmdas/sample/GNUmakefile new file mode 100644 index 0000000..2c30757 --- /dev/null +++ b/src/pmdas/sample/GNUmakefile @@ -0,0 +1,40 @@ +# +# Copyright (c) 2014 Red Hat. All Rights Reserved. +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +DFILES = README help +LSRCFILES = root pmns $(DFILES) Install Remove Sample.pmchart get_next_pmid +LDIRT = *.dir *.pag + +SUBDIRS = src +PMDADIR = $(PCP_PMDAS_DIR)/sample + +default_pcp default :: $(SUBDIRS) + +default_pcp default :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install_pcp install :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install_pcp install :: default + $(INSTALL) -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) pmns root $(PMDADIR) + $(INSTALL) -m 644 Sample.pmchart $(PCP_VAR_DIR)/config/pmchart/Sample + +include $(BUILDRULES) diff --git a/src/pmdas/sample/Install b/src/pmdas/sample/Install new file mode 100755 index 0000000..fd6bee6 --- /dev/null +++ b/src/pmdas/sample/Install @@ -0,0 +1,74 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the sample PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=sample +forced_restart=false +pmda_interface=2 + +# Do it for sample +# +pmdaSetup + +echo "======================" +echo "= sample daemon PMDA =" +echo "======================" + +daemon_opt=true # can install as daemon +dso_opt=false +pipe_opt=true # supports pipe IPC +socket_opt=true # supports socket IPC +socket_inet_def=2077 # default TCP port for Internet socket IPC + +pmdaInstall + +# The name of the PMDA for sampledso +# +iam=sampledso + +# Do it for sampledso ... no pmdaSetup +# + +echo "======================" +echo "= sampledso DSO PMDA =" +echo "======================" + +domain=30 +SYMDOM=SAMPLEDSO +sed -e 's/sample/sampledso/' -e 's/SAMPLE/SAMPLEDSO/' $tmp/pmns +pmns_source=$tmp/pmns +pmns_name=sampledso + +if $do_pmda +then + sed -e 's/sample/sampledso/' -e 's/SAMPLE/SAMPLEDSO/' dsohelp + help_source=dsohelp +fi + +dso_opt=true +daemon_opt=false +dso_suffix=so +[ "$PCP_PLATFORM" = "mingw" ] && dso_suffix=dll +[ "$PCP_PLATFORM" = "darwin" ] && dso_suffix=dylib +dso_name=$PCP_PMDAS_DIR/sample/pmda_sample.$dso_suffix +dso_entry=sample_init + +pmdaInstall + +exit 0 diff --git a/src/pmdas/sample/README b/src/pmdas/sample/README new file mode 100644 index 0000000..6e15fcf --- /dev/null +++ b/src/pmdas/sample/README @@ -0,0 +1,63 @@ +Sample PMDA +=========== + +This PMDA supports a synthetic collection of metrics that are designed +to exercise various facilities of the Performance Co-Pilot. The most +common reasons for installing this PMDA are + a. for internal product QA, or + b. as part of one of the animated tutorials or demonstrations that + accompany the PCP + +Two variants of the PMDA are installed, namely "sample" as a daemon +with an IPC channel to PMCD, and "sampledso" that is attached as a +Dynamic Shared Object (DSO) by PMCD and called directly. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT sample sampledso + +Installation +============ + + + # cd $PCP_PMDAS_DIR/sample + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + Note that sampledso uses the next domain number after the one in + ./domain.h, so you must check for its uniqueness as well. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + You will be prompted to choose the IPC channel for the daemon + implementation of the sample PMDA -- everything else is automated + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/sample + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/sample.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/sample/Remove b/src/pmdas/sample/Remove new file mode 100755 index 0000000..5b547f9 --- /dev/null +++ b/src/pmdas/sample/Remove @@ -0,0 +1,45 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the sample PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=sample + +# Do it +# +pmdaSetup +pmdaRemove + +# and again +# +iam=sampledso +pmns_name=sampledso +domain=30 +pmdaRemove + +exit 0 diff --git a/src/pmdas/sample/Sample.pmchart b/src/pmdas/sample/Sample.pmchart new file mode 100644 index 0000000..188ef1c --- /dev/null +++ b/src/pmdas/sample/Sample.pmchart @@ -0,0 +1,10 @@ +#pmchart +Version 1.2 host dynamic + +Chart Style plot + Plot Color #-cycle Host * Metric sample.drift +Chart Style plot + Plot Color #-cycle Host * Metric sample.step + +# +# Created by pmchart Mon Jul 15 08:52:18 1996 diff --git a/src/pmdas/sample/domain.h b/src/pmdas/sample/domain.h new file mode 100644 index 0000000..0aa641b --- /dev/null +++ b/src/pmdas/sample/domain.h @@ -0,0 +1,4 @@ +/* + * built from ../../pmns/stdpmid + */ +#define SAMPLE 29 diff --git a/src/pmdas/sample/get_next_pmid b/src/pmdas/sample/get_next_pmid new file mode 100755 index 0000000..5443a7d --- /dev/null +++ b/src/pmdas/sample/get_next_pmid @@ -0,0 +1,48 @@ +#!/bin/sh +# +# find next unallocated PMIDs for the sample PMDA +# + +if [ $# -gt 1 ] +then + echo "Usage: $0 [N]" + echo "N defaults to 1 (PMID to generate)" + exit 1 +fi +want=1 +[ $# -eq 1 ] && want="$1" + +root=root +if [ ! -f $root ] +then + root=../root +fi +if [ ! -f $root ] +then + echo "Error: cannot find PMNS in root nor ../root" + exit 1 +fi + +pminfo -m -n $root \ +| sed -n -e '/ 29/s/\(.*\): \(.*\)/\2 \1/p' \ +| sort -t . -n -k 1,1 -k 2,2 -k 3,3 \ +| sed -e 's/ .*//' -e 's/\./ /g' \ +| awk ' +BEGIN { lastidx = -1; found = 0 } +NF != 3 { next } +$2 != 0 { next } + { if (lastidx+1 != $3) { + for (i = lastidx+1; i < $3; i++) { + print "SAMPLE:0:" i + found++ + if (found == '"$want"') exit + } + } + lastidx = $3 + } +END { while (found < '"$want"') { + lastidx++ + print "SAMPLE:0:" lastidx + found++ + } + }' diff --git a/src/pmdas/sample/help b/src/pmdas/sample/help new file mode 100644 index 0000000..756c374 --- /dev/null +++ b/src/pmdas/sample/help @@ -0,0 +1,602 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# sample PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ SAMPLE.1 Instance domain "colour" for sample PMDA +Universally 3 instances, "red" (0), "green" (1) and "blue" (3). + +@ SAMPLE.2 Instance domain "bin" for sample PMDA +Universally 9 instances numbered 100 .. 900 in steps of 100, and named +"bin-100" .. "bin-900" + +@ SAMPLE.3 Instance domain "mirage" for sample PMDA +Random number of instances, that change with time. Instance "m-00" (0) +is always present, while the others are numbered 1 .. 49 and named "m-01" +.. "m-99" + +@ SAMPLE.4 Instance domain "family" for sample PMDA. +A fixed set of instances: + "colleen", "terry", "emma", "cathy" and "fat bald bastard" + +@ SAMPLE.7 Instance domain "many" for sample PMDA. +A varable size set of instances controlled by sample.many.count + +@ sample.control A control variable for the "sample" PMDA +This control variable may be modified using pmStore(). +The allowed values are + 0 disable debugging output in the PMDA + >0 set pmDebug to this value to enable debugging output + -1 force the PMDA to terminate + +@ sample.daemon_pid Process id of PMDA daemon +The process id of PMDA daemon, -1 if the daemon is a DSO. + +@ sample.seconds Elapsed time (seconds) +The elapsed time since the PMDA started, in seconds, i.e. as returned +by time(2). + +@ sample.milliseconds Elapsed time (milliseconds) +The elapsed time since the PMDA started, in milliseconds, i.e. as +returned by gettimeofday(2), and then adjusted from microseconds +to milliseconds. + +@ sample.load Hypothetical load +The hypothetical load is always 42! + +@ sample.colour Metrics with a "saw-tooth" trend over time +This metric has 3 instances, designated "red", "green" and "blue". + +The value of the metric is monotonic increasing in the range N to +N+100, then back to N. The different instances have different N values, +namely 100 (red), 200 (green) and 300 (blue). + +@ sample.darkness No values available +Defined over the same instance domain as sample.colour, but this metric +returns the "No values available" error for every fetch. + +@ sample.bin Several constant instances +9 instances labelled "bin-100" thru "bin-900", each with a constant +value of 100 thru 900. + +@ sample.bucket Several constant instances +9 instances labelled "bin-100" thru "bin-900", each with a constant +value of 100 thru 900. This is an alias for sample.bin, but with +a different PMID. + +@ sample.part_bin Several constant instances +5 instances labelled "bin-100" thru "bin-900", each with a constant +value of 100 thru 900. This is defined over the same domain as +sample.part, but half of the instances are missing. + +@ sample.bogus_bin Several constant instances +9 instances labelled "bin-100" thru "bin-900", each with a constant +value of 100 thru 900. This is defined over the same domain as +sample.part, half the values are for instances not in the instance +domain. + +@ sample.drift A random trended metric +This metric returns a random value (expected mean is approximately 200), +subject to a trending pattern such that the sequence is mainly monotonic, +with a change in direction after on average 4 consecutive samples. + +Use pmStore() to modifiy the instantaneous value, which becomes the new +expected mean. + +@ sample.step A step function (instantaneous) +This metric changes magnitude every 30 seconds, between a base value and +3 times the base value. + +The metric has "instantaneous" semantics. See also sample.step_counter. + +Use pmStore() to modify the base value. + +@ sample.step_counter A step function (counter) +This metric changes magnitude every 30 seconds, between a base value and +3 times the base value. + +The metric has "counter" semantics. See also sample.step. + +Use pmStore() to modify the base value. + +@ sample.needprofile Metrics that need an explicit profile +Simulate behaviour similar to the "proc" PMDA where metrics values are +only available if an explicit instance profile is provided. + +@ sample.lights Traffic lights. +A singular metric that has a discrete string value, namely "red", +"yellow" or "green". There is some persistance in the value, so +consecutive fetches are likely to return the same value, however over a +long period of time all values are equally probable. + +@ sample.magnitude Powers of two. +A singular metric that has a discrete integer value, namely 1, 2, 4, 8, +16, 32 or 64. There is some persistance in the value, so consecutive +fetches are likely to return the same value, however over a long period +of time all values are equally probable. + +@ sample.pdu Total PDU count +Count of PDUs received or transmitted. + +Use pmStore() to reset the counter to 0, independent of the value passed +to pmStore(). + +@ sample.recv_pdu Count of PDUs received +Count of PDUs received. + +Use pmStore() to reset the counter to 0, independent of the value passed +to pmStore(). + +@ sample.xmit_pdu Count of PDUs transmitted +Count of PDUs transmitted. + +Use pmStore() to reset the counter to 0, independent of the value passed +to pmStore(). + +@ sample.mirage Simple saw-tooth rate, but instances come and go +The metric is a rate (Kbytes/sec) that varies in a saw-tooth distribution +over time. Different instances of the metric have different baselines +for the saw-tooth, but all have an max-to-min range of 100. + +What makes this metric interesting is that instances come and go (not +more often than once every 10 seconds however). Instance 0 is always +present, but the other instances 1 thru 49 come and go in a cyclic +pattern with a large random component influencing when each instance +appears and disappears. + +@ sample.mirage_longlong Simple saw-tooth rate, but instances come and go +The metric is a rate (bytes/msec) that varies in a saw-tooth distribution +over time. Different instances of the metric have different baselines +for the saw-tooth, but all have an max-to-min range of 100,000,000. + +What makes this metric interesting is that instances come and go (not +more often than once every 10 seconds however). Instance 0 is always +present, but the other instances 1 thru 49 come and go in a cyclic +pattern with a large random component influencing when each instance +appears and disappears. + +@ sample.write_me Modifiable, but otherwise constant. +This metric has a 32-bit integer value of 2, unless changed via pmStore. +The metric has semantics of rate, and units of events per second. + +@ sample.sysinfo Aggregate containing system accounting structures +This metric has an aggregate value containing the following struct: + struct { + int len; + struct sysinfo sysinfo; + }; + +The len field contains the size of the structure enclosing it. +The sysinfo field contains various system accounting structures, summed over +all CPUs, as returned by + sysmp(MP_SAGET, MPSA_SINFO, ...); + +See /usr/include/sys/sysinfo.h for the definition of the sysinfo struct. + +@ sample.noinst No instance available +For testing, only. This metric is known, but no value is ever available + +@ sample.long.one 1 as a 32-bit integer +@ sample.long.ten 10 as a 32-bit integer +@ sample.long.hundred 100 as a 32-bit integer +@ sample.long.million 1000000 as a 32-bit integer +@ sample.long.write_me a 32-bit integer that can be modified +@ sample.long.bin like sample.bin but type 32 +@ sample.long.bin_ctr like sample.bin but type 32, SEM_COUNTER and SPACE_KBYTE + +@ sample.ulong.one 1 as a 32-bit unsigned integer +@ sample.ulong.ten 10 as a 32-bit unsigned integer +@ sample.ulong.hundred 100 as a 32-bit unsigned integer +@ sample.ulong.million 1000000 as a 32-bit unsigned integer +@ sample.ulong.write_me a 32-bit unsigned integer that can be modified +@ sample.ulong.bin like sample.bin but type U32 +@ sample.ulong.bin_ctr like sample.bin but type U32, SEM_COUNTER and SPACE_KBYTE + +@ sample.ulong.count.base count scale is 1, value is 42,000,000 +@ sample.ulong.count.deca count scale is 10, value is 4,200,000 +@ sample.ulong.count.hecto count scale is 10, value is 420,000 +@ sample.ulong.count.kilo count scale is 10, value is 42,000 +@ sample.ulong.count.mega count scale is 10, value is 42 + +@ sample.longlong.one 1 as a 64-bit integer +@ sample.longlong.ten 10 as a 64-bit integer +@ sample.longlong.hundred 100 as a 64-bit integer +@ sample.longlong.million 1000000 as a 64-bit integer +@ sample.longlong.write_me a 64-bit integer that can be modified +@ sample.longlong.bin like sample.bin but type 64 +@ sample.longlong.bin_ctr like sample.bin but type 64, SEM_COUNTER and SPACE_KBYTE + +@ sample.ulonglong.one 1 as a 64-bit unsigned integer +@ sample.ulonglong.ten 10 as a 64-bit unsigned integer +@ sample.ulonglong.hundred 100 as a 64-bit unsigned integer +@ sample.ulonglong.million 1000000 as a 64-bit unsigned integer +@ sample.ulonglong.write_me a 64-bit unsigned integer that can be modified +@ sample.ulonglong.bin like sample.bin but type U64 +@ sample.ulonglong.bin_ctr like sample.bin but type U64, SEM_COUNTER and SPACE_KBYTE + +@ sample.float.one 1 as a 32-bit floating point value +@ sample.float.ten 10 as a 32-bit floating point value +@ sample.float.hundred 100 as a 32-bit floating point value +@ sample.float.million 1000000 as a 32-bit floating point value +@ sample.float.write_me a 32-bit floating-point value that can be modified +@ sample.float.bin like sample.bin but type FLOAT +@ sample.float.bin_ctr like sample.bin but type FLOAT, SEM_COUNTER and SPACE_KBYTE + +@ sample.double.one 1 as a 64-bit floating point value +@ sample.double.ten 10 as a 64-bit floating point value +@ sample.double.hundred 100 as a 64-bit floating point value +@ sample.double.million 1000000 as a 64-bit floating point value +@ sample.double.write_me a 64-bit floating-point value that can be modified +@ sample.double.bin like sample.bin but type DOUBLE +@ sample.double.bin_ctr like sample.bin but type DOUBLE, SEM_COUNTER and SPACE_KBYTE + +@ sample.string.null a zero length string +@ sample.string.hullo K&R have a lot to answer for +@ sample.string.write_me a string value that can be modified + +@ sample.aggregate.null a zero length aggregate +@ sample.aggregate.hullo K&R have a lot to answer for +@ sample.aggregate.write_me a aggregate value that can be modified + +@ sample.hordes.one 500 instances +Value of the metric is the instance identifier. +@ sample.hordes.two 500 instances +Value of the metric is 500 - the instance identifier. + +@ sample.bad.unknown Not known to the PMDA +In the PMNS, but the sample agent pretends it does not know about this one. +@ sample.bad.nosupport Not supported in this version of the PMDA +Type is PM_NOSUPPORT, fetch returns PM_ERR_APPVERSION +@ sample.bad.novalues Scalar with no values, ever + +@ sample.not_ready interval (in seconds) during which PMDA does not respond to PDUs +Store a positive number of seconds as the value of this metric. The +following PDU received will result in the following sequence of events: + 1. return an error PDU with PM_ERR_PMDANOTREADY to pmcd + 2. sleep for the given number of seconds + 3. sends an error PDU with PM_ERR_PMDAREADY to pmcd +If everything went as planned, sample.not_ready returns to 0, otherwise it +has a negative error code as value. + +@ sample.wrap.long long counter that wraps +The metric value increments by INT_MAX / 2 - 1 (from ) every +time it is fetched. + +@ sample.wrap.ulong unsigned long counter that wraps +The metric value increments by UINT_MAX / 2 - 1 (from ) every +time it is fetched. + +@ sample.wrap.longlong long long counter that wraps +The metric value increments by LONGLONG_MAX / 2 - 1 (from ) +every time it is fetched. + +@ sample.wrap.ulonglong unsigned long long counter that wraps +The metric value increments by ULONGLONG_MAX / 2 - 1 (from ) +every time it is fetched. + +@ sample.dodgey.value 5 unreliable instances +The metric is a set of 5 instantaneous values, drawn at random from the +range 0 to 100. The number of instances "visible" is controlled by +sample.dodgey.control. + +@ sample.dodgey.control control values retured for sample.dodgey.value +If sample.dodgey.control is <= 0, then this is returned as the "numval" +component in the pmResult (0 => no values available, less than 0 => +various errors). + +If sample.dodgey.control is between 1 and 5 (inclusive), then this many +of the values will be "visible". The values will be selected in order +from the underlying 5 instances. + +If sample.dodgey.control is > 5, then at random times (between 1 and +sample.dodgey.control fetches of the metric), the number of instances +available is changed according to the following probabilities ... + 0.9 some number of instances in the range 0 to 5, selected at random + from the underlying 5 instances. + 0.1 error (PM_ERR_NOAGENT or PM_ERR_AGAIN or PM_ERR_APPVERSION) + +@ sample.rapid count very quickly +Base counter increments by 8*10^7 per fetch. Result is 10 x base counter. + +@ sample.scale_step.bytes_up count up by powers of 2, wrap back to one at 10 Tbytes + +@ sample.scale_step.bytes_down count down by powers of 2, wrap back to 10 Tbytes at 1 + +@ sample.scale_step.count_up count up by powers of 10, wrap back to 1 at 10e12 + +@ sample.scale_step.count_down count down by powers of 10, wrap back to 10e12 at 1 + +@ sample.scale_step.time_up_secs count up seconds by multiples of 10, wrap back to 1 second at 1 day + +@ sample.scale_step.time_up_nanosecs count up nanoseconds by multiples of 10, wrap back to 1 nanosecond at 1 day + +@ sample.scale_step.none_up count up dimensionless by multiples of 10, wrap back to 1 at 10 million + +@ sample.const_rate.value constant rate counter +A counter that changes with constant rate between fetches. + +The rate is set by storing the desired rate (counts per second) +into sample.const_rate.gradient + +@ sample.const_rate.gradient rate per second to set sample.const_rate.value, writable + +@ sample.error_code Arbitrary PMAPI error code for sample.error_check +The metrics sample.error_code and sample.error_check are used in tandem +as follows: + if sample.error_code is < 0, then any attempt to retrieve + information about sample.error_check will return a + sample.error_code as a PMAPI error from the PMDA. + +Use pmstore(1) to change sample.error_code. + +@ sample.error_check Return PMAPI error code from sample.error_code +The metrics sample.error_code and sample.error_check are used in tandem +as follows: + if sample.error_code is < 0, then any attempt to retrieve + information about sample.error_check will return a + sample.error_code as a PMAPI error from the PMDA. + +Otherwise sample.error_check is a boring metric that always has +the value 0. + +@ sample.dynamic.counter counter metric with dynamic indom +Instances come from $PCP_PMDAS_DIR/sample/dynamic.indom, if it exists. +Each line in this file is + internal_id external_id + +This metric increments each time this instance has been seen when scanning +the dynamic.indom file, and resets to zero each time the instance appears. + +@ sample.dynamic.discrete discrete metric with dynamic indom +Instances come from $PCP_PMDAS_DIR/sample/dynamic.indom, if it exists. +Each line in this file is + internal_id external_id + +This metric increments each time this instance has been seen when scanning +the dynamic.indom file, and resets to zero each time the instance appears. + +@ sample.dynamic.instant instant metric with dynamic indom +Instances come from $PCP_PMDAS_DIR/sample/dynamic.indom, if it exists. +Each line in this file is + internal_id external_id + +This metric increments each time this instance has been seen when scanning +the dynamic.indom file, and resets to zero each time the instance appears. + +@ sample.many.count number of instances in sample.many.int's domain +store a value in sample.many.count to change the number of instances +that appear in sample.many.int's instance domain + +@ sample.many.int variable sized instance domain +store a value in sample.many.count to change the number of instances +that appear in sample.many.int's instance domain + +@ sample.bigid a metric with item number bigger then 2^9 + +@ sample.byte_ctr counter byte counter +value increments randomly in the range (0,1023) bytes per fetch + +@ sample.byte_rate instantaneous bytes/second +random value in the range (0,1023), so avg value is 512 bytes/second + +@ sample.kbyte_ctr counter Kbytes/second +value increments randomly in the range (0,1023) Kbytes per fetch + +@ sample.kbyte_rate instantaneous Kbytes/second +random value in the range (0,1023), so avg value is 512 Kbytes/second + +@ sample.byte_rate_perhour instantaneous bytes/hour +random value in the range (0,1023), so avg value is 512 bytes/hour + +@ sample.dynamic.meta.metric metric with modifiable metadata +See sample.dynamic.meta.pmdesc for the metrics that can be modified to +change the metadata for this metric. +The value of this metric is always 42. + +@ sample.dynamic.meta.pmdesc.type pmDesc.type for sample.dynamic.meta.metric +One of these values: +PM_TYPE_NOSUPPORT -1 /* not implemented in this version */ +PM_TYPE_32 0 /* 32-bit signed integer */ +PM_TYPE_U32 1 /* 32-bit unsigned integer */ +PM_TYPE_64 2 /* 64-bit signed integer */ +PM_TYPE_U64 3 /* 64-bit unsigned integer */ +PM_TYPE_FLOAT 4 /* 32-bit floating point */ +PM_TYPE_DOUBLE 5 /* 64-bit floating point */ +PM_TYPE_STRING 6 /* array of char */ +PM_TYPE_AGGREGATE 7 /* arbitrary binary data (aggregate) */ +PM_TYPE_AGGREGATE_STATIC 8 /* static pointer to aggregate */ +PM_TYPE_UNKNOWN 255 /* used in pmValueBlock, not pmDesc */ + +Defaults to PM_TYPE_32. + +@ sample.dynamic.meta.pmdesc.indom pmDesc.indom for sample.dynamic.meta.metric +Defaults to PM_INDOM_NULL (0xffffffff). + +@ sample.dynamic.meta.pmdesc.sem pmDesc.sem for sample.dynamic.meta.metric +One of these values: +PM_SEM_COUNTER 1 /* cumulative counter (monotonic increasing) */ +PM_SEM_INSTANT 3 /* instantaneous value, continuous domain */ +PM_SEM_DISCRETE 4 /* instantaneous value, discrete domain */ + +Defaults to PM_SEM_DISCRETE. + +@ sample.dynamic.meta.pmdesc.units pmDesc.units for sample.dynamic.meta.metric +6 x 4-bit values, from least-significant bit to most-significant bit: +dimSpace: + -1, 0, 1 +dimTime: + -1, 0, 1 +dimCount: + 0, 1 +scaleSpace: + PM_SPACE_BYTE 0 /* bytes */ + PM_SPACE_KBYTE 1 /* Kilobytes (1024) */ + PM_SPACE_MBYTE 2 /* Megabytes (1024^2) */ + PM_SPACE_GBYTE 3 /* Gigabytes (1024^3) */ + PM_SPACE_TBYTE 4 /* Terabytes (1024^4) */ + PM_SPACE_PBYTE 5 /* Petabytes (1024^5) */ + PM_SPACE_EBYTE 6 /* Exabytes (1024^6) */ +scaleTime: + PM_TIME_NSEC 0 /* nanoseconds */ + PM_TIME_USEC 1 /* microseconds */ + PM_TIME_MSEC 2 /* milliseconds */ + PM_TIME_SEC 3 /* seconds */ + PM_TIME_MIN 4 /* minutes */ + PM_TIME_HOUR 5 /* hours */ +scaleCount: + PM_COUNT_ONE 0 /* 1 */ + +Defaults to { 1, -1, 0, PM_SPACE_BYTE, PM_TIME_SEC, 0 } + +@ sample.datasize Space allocated for PMDA's data segment +This metric returns the amount of memory in kilobytes allocated for the +data segment of the PMDA. + +This is handy for tracing memory utilization (and leaks) in libpcp_pmda. + +@ SAMPLE.0.1000 dynamic *.secret.bar metric +Value "foo". + +@ SAMPLE.0.1001 dynamic *.secret.foo.one metric +Value 1. + +@ SAMPLE.0.1002 dynamic *.secret.foo.two metric +Value 2. + +@ SAMPLE.0.1003 dynamic *.secret.foo.bar.three metric +Value 3. + +@ SAMPLE.0.1004 dynamic *.secret.foo.bar.four metric +Value 4. + +@ SAMPLE.0.1005 dynamic *.secret.foo.bar.grunt.five metric +Value 5. + +@ SAMPLE.0.1006 dynamic *.secret.foo.bar.grunt.snort.six metric +Value 6. + +@ SAMPLE.0.1007 dynamic *.secret.foo.bar.grunt.snort.huff.puff.seven metric +Value 7. + +@ sample.scramble.bin Several constant instances, instances scrambled +Like sample.bin, except +1. instances are missing with probability 0.33 +2. order of the instances from pmFetch is random + +Designed to help testing instance matching between pmFetch calls +for PCP clients. + +@ sample.scramble.version Current state version and reset for sample.scramble.bin +To make the order of instances seen from sample.scramble.bin +deterministic, use pmstore(1) to trigger a reset. + +@ sample.percontext.control.ctx Number of PMAPI contexts seen +One more than the highest PMAPI context number from PMCD. + +@ sample.percontext.control.active Number of active PMAPI contexts + +@ sample.percontext.control.start Number of new PMAPI contexts seen +Incremented each time a new PMAPI context is seen from PMCD. + +sample.percontext.control.start - sample.percontext.control.end +should equal sample.percontext.control.active. + +@ sample.percontext.control.end Number of PMAPI contexts closed +Incremented each time PMCD closes a PMAPI context. + +sample.percontext.control.start - sample.percontext.control.end +should equal sample.percontext.control.active. + +@ sample.percontext.pdu Total PDU count for the client context +Count of PDUs received from or transmitted to the current PMAPI client +context. + +Use pmStore() to reset the counter to 0, independent of the value passed +to pmStore(). + +@ sample.percontext.recv_pdu Count of PDUs received from the client context +Count of PDUs received from the current PMAPI client context. + +Use pmStore() to reset the counter to 0, independent of the value passed +to pmStore(). + +@ sample.percontext.xmit_pdu Count of PDUs transmitted +Count of PDUs transmitted to the current PMAPI client context. + +Use pmStore() to reset the counter to 0, independent of the value passed +to pmStore(). + +@ sample.event.records Dummy event records +Dummy event records are generated in a fixed pattern to help QA. + +Once all setups have been returned, the cycle is repeated. + +See event.reset to exert explicit control over the next batch of event +records to be returned. + +@ sample.event.no_indom_records More dummy event records +Like sample.event.records but without the instance domain. + +@ sample.event.reset reset event record state +Used for QA, should take one of the values 0, 1, 2 or 3 +to determine which of the dummy event record setups will +be returned for the next fetch of event.records. + +@ sample.event.highres_records Dummy highres timestamp event records +Dummy high resolution event records generated in a fixed pattern to help QA. + +Once all setups have been returned, the cycle is repeated. + +See event.reset_highres to exert explicit control over the next batch of event +records to be returned. + +@ sample.event.reset_highres reset highres event record state +Used for QA, should take one of the values 0, 1, 2 or 3 +to determine which of the dummy event record setups will +be returned for the next fetch of event.highres_records. + +@ sample.event.type event type parameter for event records + +@ sample.event.param_32 32 parameter for event records + +@ sample.event.param_u32 U32 parameter for event records + +@ sample.event.param_64 64 parameter for event records + +@ sample.event.param_u64 U64 parameter for event records + +@ sample.event.param_float FLOAT parameter for event records + +@ sample.event.param_double DOUBLE parameter for event records + +@ sample.event.param_string STRING parameter for event records + +@ sample.event.param_aggregate AGGREGATE parameter for event records diff --git a/src/pmdas/sample/pmns b/src/pmdas/sample/pmns new file mode 100644 index 0000000..87c7507 --- /dev/null +++ b/src/pmdas/sample/pmns @@ -0,0 +1,260 @@ +/* + * Metrics for sample PMDA + * + * Next pmid ... use get_next_pmid + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +sample { + control SAMPLE:0:0 + daemon_pid SAMPLE:0:1 + seconds SAMPLE:0:2 + milliseconds SAMPLE:0:3 + load SAMPLE:0:4 + colour SAMPLE:0:5 + darkness SAMPLE:0:92 + bin SAMPLE:0:6 + bucket SAMPLE:0:48 + part_bin SAMPLE:0:50 + bogus_bin SAMPLE:0:51 + drift SAMPLE:0:7 + step SAMPLE:0:8 + step_counter SAMPLE:0:63 + mirage SAMPLE:0:37 + mirage_longlong SAMPLE:0:38 + write_me SAMPLE:0:36 + lights SAMPLE:0:46 + magnitude SAMPLE:0:47 + sysinfo SAMPLE:0:39 + pdu SAMPLE:0:40 + recv_pdu SAMPLE:0:41 + xmit_pdu SAMPLE:0:42 + noinst SAMPLE:0:9 + needprofile SAMPLE:0:49 + long + ulong + longlong + ulonglong + float + double + string + aggregate + hordes + bad + not_ready SAMPLE:0:56 + wrap + dodgey + dynamic + rapid SAMPLE:0:64 + scale_step + const_rate + error_code SAMPLE:0:74 + error_check SAMPLE:0:75 + bigid SAMPLE:0:1023 + many + byte_ctr SAMPLE:0:81 + byte_rate SAMPLE:0:82 + kbyte_ctr SAMPLE:0:83 + kbyte_rate SAMPLE:0:84 + byte_rate_perhour SAMPLE:0:85 + datasize SAMPLE:0:91 + secret SAMPLE:*:* + scramble + percontext + event +} + +sample.long { + one SAMPLE:0:10 + ten SAMPLE:0:11 + hundred SAMPLE:0:12 + million SAMPLE:0:13 + write_me SAMPLE:0:14 + bin SAMPLE:0:103 + bin_ctr SAMPLE:0:104 +} + +sample.ulong { + one SAMPLE:0:93 + ten SAMPLE:0:94 + hundred SAMPLE:0:95 + million SAMPLE:0:96 + write_me SAMPLE:0:97 + bin SAMPLE:0:105 + bin_ctr SAMPLE:0:106 + count +} + +sample.ulong.count { + base SAMPLE:0:115 + deca SAMPLE:0:116 + hecto SAMPLE:0:117 + kilo SAMPLE:0:118 + mega SAMPLE:0:119 +} + +sample.float { + one SAMPLE:0:15 + ten SAMPLE:0:16 + hundred SAMPLE:0:17 + million SAMPLE:0:18 + write_me SAMPLE:0:19 + bin SAMPLE:0:107 + bin_ctr SAMPLE:0:108 +} + +sample.longlong { + one SAMPLE:0:20 + ten SAMPLE:0:21 + hundred SAMPLE:0:22 + million SAMPLE:0:23 + write_me SAMPLE:0:24 + bin SAMPLE:0:109 + bin_ctr SAMPLE:0:110 +} + +sample.ulonglong { + one SAMPLE:0:98 + ten SAMPLE:0:99 + hundred SAMPLE:0:100 + million SAMPLE:0:101 + write_me SAMPLE:0:102 + bin SAMPLE:0:111 + bin_ctr SAMPLE:0:112 +} + +sample.double { + one SAMPLE:0:25 + ten SAMPLE:0:26 + hundred SAMPLE:0:27 + million SAMPLE:0:28 + write_me SAMPLE:0:29 + bin SAMPLE:0:113 + bin_ctr SAMPLE:0:114 +} + +sample.string { + null SAMPLE:0:30 + hullo SAMPLE:0:31 + write_me SAMPLE:0:32 +} + +sample.aggregate { + null SAMPLE:0:33 + hullo SAMPLE:0:34 + write_me SAMPLE:0:35 +} + +sample.hordes { + one SAMPLE:0:52 + two SAMPLE:0:53 +} + +sample.bad { + unknown SAMPLE:0:54 + nosupport SAMPLE:0:55 + novalues SAMPLE:0:138 +} + +sample.wrap { + long SAMPLE:0:57 + ulong SAMPLE:0:58 + longlong SAMPLE:0:59 + ulonglong SAMPLE:0:60 +} + +sample.dodgey { + control SAMPLE:0:61 + value SAMPLE:0:62 +} + +sample.scale_step { + bytes_up SAMPLE:0:65 + bytes_down SAMPLE:0:66 + count_up SAMPLE:0:67 + count_down SAMPLE:0:68 + time_up_secs SAMPLE:0:69 + time_up_nanosecs SAMPLE:0:70 + none_up SAMPLE:0:71 +} + +sample.const_rate { + value SAMPLE:0:72 + gradient SAMPLE:0:73 +} + +sample.dynamic { + counter SAMPLE:0:76 + discrete SAMPLE:0:77 + instant SAMPLE:0:78 + meta +} + +sample.dynamic.meta { + metric SAMPLE:0:86 + pmdesc +} + +sample.dynamic.meta.pmdesc { + type SAMPLE:0:87 + indom SAMPLE:0:88 + sem SAMPLE:0:89 + units SAMPLE:0:90 +} + +sample.many { + count SAMPLE:0:79 + int SAMPLE:0:80 +} + +sample.scramble { + version SAMPLE:0:120 + bin SAMPLE:0:121 +} + +sample.percontext { + control + pdu SAMPLE:0:43 + recv_pdu SAMPLE:0:44 + xmit_pdu SAMPLE:0:45 +} + +sample.percontext.control { + ctx SAMPLE:0:122 + active SAMPLE:0:123 + start SAMPLE:0:124 + end SAMPLE:0:125 +} + +sample.event { + records SAMPLE:0:136 + highres_records SAMPLE:0:139 + no_indom_records SAMPLE:0:137 + reset SAMPLE:0:126 + reset_highres SAMPLE:0:140 + type SAMPLE:0:127 + param_32 SAMPLE:0:128 + param_u32 SAMPLE:0:129 + param_64 SAMPLE:0:130 + param_u64 SAMPLE:0:131 + param_float SAMPLE:0:132 + param_double SAMPLE:0:133 + param_string SAMPLE:0:134 + param_aggregate SAMPLE:0:135 +} diff --git a/src/pmdas/sample/root b/src/pmdas/sample/root new file mode 100644 index 0000000..4f87c8e --- /dev/null +++ b/src/pmdas/sample/root @@ -0,0 +1,20 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { + sample + sampledso +} + +#include "pmns" + +#define sample sampledso +#ifdef SAMPLE +#undef SAMPLE +#endif +#define SAMPLE 30 +#include "pmns" + diff --git a/src/pmdas/sample/src/GNUmakefile b/src/pmdas/sample/src/GNUmakefile new file mode 100644 index 0000000..78ed5ad --- /dev/null +++ b/src/pmdas/sample/src/GNUmakefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +# +# The TARGETS macro must be set to the name of the executable we +# are building. +# +IAM = sample +DOMAIN = SAMPLE +CMDTARGET = pmda$(IAM)$(EXECSUFFIX) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +TARGETS = $(CMDTARGET) $(LIBTARGET) + +PMDAINIT = $(IAM)_init +PMDADIR = $(PCP_PMDAS_DIR)/sample + +CFILES = pmda.c sample.c percontext.c events.c +HFILES = percontext.h events.h +VERSION_SCRIPT = exports + +LDIRT = domain.h sample.log $(TARGETS) $(VERSION_SCRIPT) +LCFLAGS += $(INVISIBILITY) +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_RT) +LSRCFILES = GNUmakefile.install + +default: domain.h $(TARGETS) + +pmda.o sample.o percontext.o: percontext.h +sample.o events.o: events.h +pmda.o: $(VERSION_SCRIPT) + +domain.h: ../../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h $(CFILES) $(HFILES) $(PMDADIR) + $(INSTALL) -m 644 GNUmakefile.install $(PMDADIR)/Makefile + +include $(BUILDRULES) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/sample/src/GNUmakefile.install b/src/pmdas/sample/src/GNUmakefile.install new file mode 100644 index 0000000..37191d0 --- /dev/null +++ b/src/pmdas/sample/src/GNUmakefile.install @@ -0,0 +1,53 @@ +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +SHELL = sh + +ifdef PCP_CONF +include $(PCP_CONF) +else +include $(PCP_DIR)/etc/pcp.conf +endif +include $(PCP_INC_DIR)/builddefs + +# remove -Lpath and -Ipath options from builddefs CFLAGS value +# +PCP_LIBS = +TMP := $(CFLAGS:-I%=) +ifdef PCP_DIR +# put -Ipath and -Lpath back but use paths for run-time environment +# +CFLAGS = $(TMP) -I$(PCP_INC_DIR)/.. +LDFLAGS = -L$(PCP_LIB_DIR) +else +CFLAGS = $(TMP) +endif + +IAM = sample +CFILES = pmda.c sample.c percontext.c events.c +HFILES = percontext.h events.h + +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +CMDTARGET = pmda$(IAM) +TARGETS = $(LIBTARGET) $(CMDTARGET) + +LLDLIBS = -lpcp_pmda -lpcp $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) $(LIB_FOR_RT) +LDIRT = *.log help.dir help.pag + +default: $(TARGETS) + +install: default + +include $(PCP_INC_DIR)/buildrules diff --git a/src/pmdas/sample/src/events.c b/src/pmdas/sample/src/events.c new file mode 100644 index 0000000..cc1cf62 --- /dev/null +++ b/src/pmdas/sample/src/events.c @@ -0,0 +1,477 @@ +/* + * The "event" records here are all fake. But the logic does show how + * a real PMDA could deliver values for metrics of type PM_TYPE_EVENT + * and PM_TYPE_HIGHRES_EVENT. + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2010 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "events.h" + +static int nfetch; +static int xnfetch; +static int myarray[2]; + +static int nhrfetch; +static int xnhrfetch; +static int hrarray[2]; + +/* event parameters */ +static pmValueBlock *aggr; +static char aggrval[] = { '\01', '\03', '\07', '\017', '\037', '\077', '\177', '\377' }; +static pmID pmid_type = PMDA_PMID(0,127); /* event.type */ +static pmID pmid_32 = PMDA_PMID(0,128); /* event.param_32 */ +static pmID pmid_u32 = PMDA_PMID(0,129); /* event.param_u32 */ +static pmID pmid_64 = PMDA_PMID(0,130); /* event.param_64 */ +static pmID pmid_u64 = PMDA_PMID(0,131); /* event.param_u64 */ +static pmID pmid_float = PMDA_PMID(0,132); /* event.param_float */ +static pmID pmid_double = PMDA_PMID(0,133); /* event.param_double */ +static pmID pmid_string = PMDA_PMID(0,134); /* event.param_string */ +static pmID pmid_aggregate = PMDA_PMID(0,135); /* event.param_aggregate */ + +void +init_events(int domain) +{ + int i, sts; + + /* + * fix the domain field in the event parameter PMIDs ... + * note these PMIDs must match the corresponding metrics in + * desctab[] and this cannot easily be done automatically + */ + ((__pmID_int *)&pmid_type)->domain = domain; + ((__pmID_int *)&pmid_32)->domain = domain; + ((__pmID_int *)&pmid_u32)->domain = domain; + ((__pmID_int *)&pmid_64)->domain = domain; + ((__pmID_int *)&pmid_u64)->domain = domain; + ((__pmID_int *)&pmid_float)->domain = domain; + ((__pmID_int *)&pmid_double)->domain = domain; + ((__pmID_int *)&pmid_string)->domain = domain; + ((__pmID_int *)&pmid_aggregate)->domain = domain; + + /* build pmValueBlock for aggregate value */ + aggr = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE + sizeof(aggrval)); + aggr->vtype = PM_TYPE_AGGREGATE; + aggr->vlen = PM_VAL_HDR_SIZE + sizeof(aggrval); + memcpy(aggr->vbuf, (void *)aggrval, sizeof(aggrval)); + + /* setup event arrays for 2 instances */ + for (i = 0; i < 2; i++) { + if ((sts = myarray[i] = pmdaEventNewArray()) < 0) + fprintf(stderr, "pmdaEventNewArray: %s\n", pmErrStr(sts)); + if ((sts = hrarray[i] = pmdaEventNewHighResArray()) < 0) + fprintf(stderr, "pmdaEventNewHighResArray: %s\n", pmErrStr(sts)); + } +} + +int +event_get_fetch_count(void) +{ + return nfetch % 4; +} + +void +event_set_fetch_count(int c) +{ + xnfetch = nfetch = c; +} + +int +sample_fetch_events(pmValueBlock **vbpp, int inst) +{ + int c; + int sts; + int flags; + struct timeval stamp; + pmAtomValue atom; + + if (nfetch >= 0) + c = nfetch % 4; + else /* one of the error injection cases */ + c = nfetch; + + if (inst == PM_IN_NULL || inst > 1 || inst < 0) + inst = 1; + + pmdaEventResetArray(myarray[inst]); + gettimeofday(&stamp, NULL); + + if (inst == 0) { + /* original instance ... */ + /* rebase event records 10 secs in past, add 1 sec for each new record */ + stamp.tv_sec -= 10; + + switch (c) { + case 0: + /* + * 1st fetch + * No events + */ + break; + case 1: + /* + * 2nd fetch + * 1 event with NO parameters + */ + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + break; + case 2: + /* + * 3rd fetch + * 1 event with one U32 parameter + * 1 event with 2 parameters(U32 and 64 types) + */ + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 1; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 2; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.ll = -3; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_64, PM_TYPE_64, &atom)) < 0) + return sts; + break; + case 3: + /* + * 4th fetch + * 1 event start with 3 parameters (U32, U64 and STRING types) + * 1 event with 3 parameters (U32 and 2 DOUBLE types) + * 1 event end with 6 (U32, U64, STRING, STRING, 32 and U32 types) + * 7 "missed" events + * 1 event with 3 parameters (U32, FLOAT and AGGREGATE types) + */ + flags = PM_EVENT_FLAG_START|PM_EVENT_FLAG_ID|PM_EVENT_FLAG_PARENT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 4; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.ull = 5; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_u64, PM_TYPE_U64, &atom)) < 0) + return sts; + atom.cp = "6"; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 7; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.d = 8; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_double, PM_TYPE_DOUBLE, &atom)) < 0) + return sts; + atom.d = -9; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_double, PM_TYPE_DOUBLE, &atom)) < 0) + return sts; + flags = PM_EVENT_FLAG_END; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 10; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.ull = 11; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_u64, PM_TYPE_U64, &atom)) < 0) + return sts; + atom.cp = "twelve"; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + atom.cp = "thirteen"; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + atom.l = -14; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_32, PM_TYPE_32, &atom)) < 0) + return sts; + atom.ul = 15; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_u32, PM_TYPE_U32, &atom)) < 0) + return sts; + /* "missed" 7 records */ + if ((sts = pmdaEventAddMissedRecord(myarray[inst], &stamp, 7)) < 0) + return sts; + stamp.tv_sec++; + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 16; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.f = -17; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_float, PM_TYPE_FLOAT, &atom)) < 0) + return sts; + atom.vbp = aggr; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_aggregate, PM_TYPE_AGGREGATE, &atom)) < 0) + return sts; + break; + case -1: + /* error injection */ + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = c; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + /* pmid that is not in PMNS and not known to the PMDA */ + if ((sts = pmdaEventAddParam(myarray[inst], PMDA_PMID(100,200), PM_TYPE_U32, &atom)) < 0) + return sts; + break; + } + nfetch++; + } + else { + /* + * new, boring instance ..., either instance ["bogus"] for + * sample.events.rrecord or singular instance for + * sample.events.no_indom_records + */ + static char hrecord1[20]; + static char hrecord2[] = "bingo!"; + + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + snprintf(hrecord1, sizeof(hrecord1), "fetch #%d", xnfetch); + atom.cp = hrecord1; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + if ((xnfetch % 3) == 0) { + if ((sts = pmdaEventAddRecord(myarray[inst], &stamp, flags)) < 0) + return sts; + atom.cp = hrecord2; + if ((sts = pmdaEventAddParam(myarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + } + xnfetch++; + } + + *vbpp = (pmValueBlock *)pmdaEventGetAddr(myarray[inst]); + + return 0; +} + +int +event_get_highres_fetch_count(void) +{ + return nhrfetch % 4; +} + +void +event_set_highres_fetch_count(int c) +{ + xnhrfetch = nhrfetch = c; +} + +int +sample_fetch_highres_events(pmValueBlock **vbpp, int inst) +{ + int c; + int sts; + int flags; + struct timespec stamp; + pmAtomValue atom; + + if (nhrfetch >= 0) + c = nhrfetch % 4; + else /* one of the error injection cases */ + c = nhrfetch; + + if (inst == PM_IN_NULL || inst > 1 || inst < 0) + inst = 1; + + pmdaEventResetHighResArray(hrarray[inst]); + __pmGetTimespec(&stamp); + + if (inst == 0) { + /* original instance ... */ + /* rebase event records 10 secs in past, add 1 sec for each new record */ + stamp.tv_sec -= 10; + + switch (c) { + case 0: + /* + * 1st fetch + * No events + */ + break; + case 1: + /* + * 2nd fetch + * 1 event with NO parameters + */ + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + break; + case 2: + /* + * 3rd fetch + * 1 event with one U32 parameter + * 1 event with 2 parameters(U32 and 64 types) + */ + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 1; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 2; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.ll = -3; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_64, PM_TYPE_64, &atom)) < 0) + return sts; + break; + case 3: + /* + * 4th fetch + * 1 event start with 3 parameters (U32, U64 and STRING types) + * 1 event with 3 parameters (U32 and 2 DOUBLE types) + * 1 event end with 6 (U32, U64, STRING, STRING, 32 and U32 types) + * 7 "missed" events + * 1 event with 3 parameters (U32, FLOAT and AGGREGATE types) + */ + flags = PM_EVENT_FLAG_START|PM_EVENT_FLAG_ID|PM_EVENT_FLAG_PARENT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 4; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.ull = 5; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_u64, PM_TYPE_U64, &atom)) < 0) + return sts; + atom.cp = "6"; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 7; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.d = 8; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_double, PM_TYPE_DOUBLE, &atom)) < 0) + return sts; + atom.d = -9; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_double, PM_TYPE_DOUBLE, &atom)) < 0) + return sts; + flags = PM_EVENT_FLAG_END; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 10; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.ull = 11; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_u64, PM_TYPE_U64, &atom)) < 0) + return sts; + atom.cp = "twelve"; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + atom.cp = "thirteen"; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + atom.l = -14; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_32, PM_TYPE_32, &atom)) < 0) + return sts; + atom.ul = 15; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_u32, PM_TYPE_U32, &atom)) < 0) + return sts; + /* "missed" 7000 records */ + if ((sts = pmdaEventAddHighResMissedRecord(hrarray[inst], &stamp, 7000)) < 0) + return sts; + stamp.tv_sec++; + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = 16; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + atom.f = -17; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_float, PM_TYPE_FLOAT, &atom)) < 0) + return sts; + atom.vbp = aggr; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_aggregate, PM_TYPE_AGGREGATE, &atom)) < 0) + return sts; + break; + case -1: + /* error injection */ + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + stamp.tv_sec++; + atom.ul = c; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_type, PM_TYPE_U32, &atom)) < 0) + return sts; + /* pmid that is not in PMNS and not known to the PMDA */ + if ((sts = pmdaEventHighResAddParam(hrarray[inst], PMDA_PMID(100,200), PM_TYPE_U32, &atom)) < 0) + return sts; + break; + } + nhrfetch++; + } + else { + /* + * new, boring instance ..., either instance ["bogus"] for + * sample.events.rrecord or singular instance for + * sample.events.no_indom_records + */ + static char record1[20]; + static char record2[] = "bingo!"; + + flags = PM_EVENT_FLAG_POINT; + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + snprintf(record1, sizeof(record1), "fetch #%d", xnhrfetch); + atom.cp = record1; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + if ((xnhrfetch % 3) == 0) { + if ((sts = pmdaEventAddHighResRecord(hrarray[inst], &stamp, flags)) < 0) + return sts; + atom.cp = record2; + if ((sts = pmdaEventHighResAddParam(hrarray[inst], pmid_string, PM_TYPE_STRING, &atom)) < 0) + return sts; + } + xnhrfetch++; + } + + *vbpp = (pmValueBlock *)pmdaEventHighResGetAddr(hrarray[inst]); + + return 0; +} diff --git a/src/pmdas/sample/src/events.h b/src/pmdas/sample/src/events.h new file mode 100644 index 0000000..d7dd1b5 --- /dev/null +++ b/src/pmdas/sample/src/events.h @@ -0,0 +1,33 @@ +/* + * The "event" records here are all fake. But the logic does show how + * a real PMDA could deliver values for metrics of type PM_TYPE_EVENT. + * and PM_TYPE_HIGHRES_EVENT. + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2010 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef _EVENTS_H +#define _EVENTS_H + +extern void init_events(int); + +extern int sample_fetch_events(pmValueBlock **, int); +extern void event_set_fetch_count(int); +extern int event_get_fetch_count(void); + +extern int sample_fetch_highres_events(pmValueBlock **, int); +extern void event_set_highres_fetch_count(int); +extern int event_get_highres_fetch_count(void); + +#endif /* _EVENTS_H */ diff --git a/src/pmdas/sample/src/percontext.c b/src/pmdas/sample/src/percontext.c new file mode 100644 index 0000000..0c549df --- /dev/null +++ b/src/pmdas/sample/src/percontext.c @@ -0,0 +1,256 @@ +/* + * Some functions become per-client (of pmcd) with the introduction + * of PMDA_INTERFACE_5. + * + * Copyright (c) 2010 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "percontext.h" + +typedef struct { + int state; /* active or inactive context */ + int recv_pdu; /* count of PDUs received from this context */ + int xmit_pdu; /* count of PDUs sent to this context */ +} perctx_t; + +/* values for state */ +#define CTX_INACTIVE 0 +#define CTX_ACTIVE 1 + +static perctx_t *ctxtab; +static int num_ctx; +static int num_start; /* count of new contexts noted */ +static int num_end; /* count of end context events */ +static int num_recv_pdu; /* recv count from closed contexts */ +static int num_xmit_pdu; /* xmit count from closed contexts */ + +void +sample_clr_recv(int ctx) +{ + if (ctx == CTX_ALL) { + int i; + for (i = 0; i < num_ctx; i++) { + if (ctxtab[i].state == CTX_ACTIVE) + ctxtab[i].recv_pdu = 0; + } + num_recv_pdu = 0; + } + else if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + fprintf(stderr, "Botch: sample_clr_recv(%d) num_ctx=%d", ctx, num_ctx); + if (ctx >= 0 && ctx < num_ctx) + fprintf(stderr, " ctxtab[] is inactive"); + fputc('\n', stderr); + } + else + ctxtab[ctx].recv_pdu = 0; +} + +void +sample_clr_xmit(int ctx) +{ + if (ctx == CTX_ALL) { + int i; + for (i = 0; i < num_ctx; i++) { + if (ctxtab[i].state == CTX_ACTIVE) + ctxtab[i].xmit_pdu = 0; + } + num_xmit_pdu = 0; + } + else if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + fprintf(stderr, "Botch: sample_clr_xmit(%d) num_ctx=%d", ctx, num_ctx); + if (ctx >= 0 && ctx < num_ctx) + fprintf(stderr, " ctxtab[] is inactive"); + fputc('\n', stderr); + } + else + ctxtab[ctx].xmit_pdu = 0; +} + +int +sample_get_recv(int ctx) +{ + if (ctx == CTX_ALL) { + int i; + int ans = num_recv_pdu; + for (i = 0; i < num_ctx; i++) { + if (ctxtab[i].state == CTX_ACTIVE) + ans += ctxtab[i].recv_pdu; + } + return ans; + } + else if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + return PM_ERR_NOCONTEXT; + } + else + return ctxtab[ctx].recv_pdu; +} + +int +sample_get_xmit(int ctx) +{ + if (ctx == CTX_ALL) { + int i; + int ans = num_xmit_pdu; + for (i = 0; i < num_ctx; i++) { + if (ctxtab[i].state == CTX_ACTIVE) + ans += ctxtab[i].xmit_pdu; + } + return ans; + } + else if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + return PM_ERR_NOCONTEXT; + } + else + return ctxtab[ctx].xmit_pdu; +} + +static void +growtab(int ctx) +{ + ctxtab = (perctx_t *)realloc(ctxtab, (ctx+1)*sizeof(ctxtab[0])); + if (ctxtab == NULL) { + __pmNoMem("growtab", (ctx+1)*sizeof(ctxtab[0]), PM_FATAL_ERR); + /*NOTREACHED*/ + } + while (num_ctx <= ctx) { + ctxtab[num_ctx].state = CTX_INACTIVE; + ctxtab[num_ctx].recv_pdu = 0; + ctxtab[num_ctx].xmit_pdu = 0; + num_ctx++; + } + ctxtab[ctx].state = CTX_INACTIVE; +} + +void +sample_inc_recv(int ctx) +{ + if (ctx < 0) { + fprintf(stderr, "Botch: sample_inc_recv(%d)!\n", ctx); + return; + } + if (ctx >= num_ctx) growtab(ctx); + if (ctxtab[ctx].state == CTX_INACTIVE) { + num_start++; + ctxtab[ctx].state = CTX_ACTIVE; + ctxtab[ctx].recv_pdu = 0; + ctxtab[ctx].xmit_pdu = 0; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "sample_inc_recv(%d) [new context, num_ctx=%d]\n", ctx, num_ctx); + } +#endif + } + ctxtab[ctx].recv_pdu++; +} + +void +sample_inc_xmit(int ctx) +{ + if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + fprintf(stderr, "Botch: sample_inc_xmit(%d) num_ctx=%d", ctx, num_ctx); + if (ctx >= 0 && ctx < num_ctx) + fprintf(stderr, " ctxtab[] is inactive"); + fputc('\n', stderr); + return; + } + if (ctx >= num_ctx) + growtab(ctx); + ctxtab[ctx].xmit_pdu++; +} + +int +sample_ctx_fetch(int ctx, int item) +{ + if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + fprintf(stderr, "Botch: sample_ctx_fetch(%d, %d) num_ctx=%d", ctx, item, num_ctx); + if (ctx >= 0 && ctx < num_ctx) + fprintf(stderr, " ctxtab[] is inactive"); + fputc('\n', stderr); + return PM_ERR_NOCONTEXT; + } + + if (item == 43) { + /* percontext.pdu */ + return ctxtab[ctx].recv_pdu + ctxtab[ctx].xmit_pdu; + } + else if (item == 44) { + /* percontext.recv-pdu */ + return ctxtab[ctx].recv_pdu; + } + else if (item == 45) { + /* percontext.xmit-pdu */ + return ctxtab[ctx].xmit_pdu; + } + else if (item == 122) { + /* percontext.control.ctx */ + return num_ctx; + } + else if (item == 123) { + /* percontext.control.active */ + int i; + int ans = 0; + for (i = 0; i < num_ctx; i++) { + if (ctxtab[i].state == CTX_ACTIVE) + ans++; + } + return ans; + } + else if (item == 124) { + /* percontext.control.start */ + return num_start; + } + else if (item == 125) { + /* percontext.control.end */ + return num_end; + } + + fprintf(stderr, "Botch: sample_ctx_fetch(%d, %d): item bad!\n", ctx, item); + return PM_ERR_PMID; +} + +void +sample_ctx_end(int ctx) +{ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "sample_ctx_end(%d) [context is ", ctx); + if (ctx < 0 || ctx >= num_ctx) + fprintf(stderr, "unknown, num_ctx=%d", num_ctx); + else if (ctxtab[ctx].state == CTX_ACTIVE) + fprintf(stderr, "active"); + else if (ctxtab[ctx].state == CTX_INACTIVE) + fprintf(stderr, "inactive"); + else + fprintf(stderr, "botched state, %d", ctxtab[ctx].state); + fprintf(stderr, "]\n"); + } +#endif + if (ctx < 0 || ctx >= num_ctx || ctxtab[ctx].state == CTX_INACTIVE) { + /* + * This is expected ... when a context is closed in pmcd + * (or for a local context or for dbpmda or ...) all the + * PMDAs with a registered pmdaEndContextCallBack will be + * called end some of the PMDAs may not have not serviced + * any previous requests for that context. + */ + return; + } + num_end++; + num_recv_pdu += ctxtab[ctx].recv_pdu; + num_xmit_pdu += ctxtab[ctx].xmit_pdu; + ctxtab[ctx].state = CTX_INACTIVE; +} + diff --git a/src/pmdas/sample/src/percontext.h b/src/pmdas/sample/src/percontext.h new file mode 100644 index 0000000..151fba6 --- /dev/null +++ b/src/pmdas/sample/src/percontext.h @@ -0,0 +1,31 @@ +/* + * Some functions become per-client (of pmcd) with the introduction + * of PMDA_INTERFACE_5. + * + * Copyright (c) 2010 Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef _PERCONTEXT_H +#define _PERCONTEXT_H + +extern int sample_get_recv(int); +extern int sample_get_xmit(int); +extern void sample_clr_recv(int); +extern void sample_clr_xmit(int); +extern void sample_inc_recv(int); +extern void sample_inc_xmit(int); +extern int sample_ctx_fetch(int, int); +extern void sample_ctx_end(int); + +#define CTX_ALL -1 + +#endif /* _PERCONTEXT_H */ diff --git a/src/pmdas/sample/src/pmda.c b/src/pmdas/sample/src/pmda.c new file mode 100644 index 0000000..c1449fb --- /dev/null +++ b/src/pmdas/sample/src/pmda.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Generic driver for a daemon-based PMDA + */ + +#include +#include +#include +#include "domain.h" +#include "percontext.h" + +extern void sample_init(pmdaInterface *); +extern int sample_done; +extern int not_ready; +extern int _isDSO; + +static pmdaInterface dispatch; +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_TEXT("\nExactly one of the following options may appear:"), + PMDAOPT_INET, + PMDAOPT_PIPE, + PMDAOPT_UNIX, + PMDAOPT_IPV6, + PMDA_OPTIONS_END +}; +static pmdaOptions opts = { + .short_options = "D:d:i:l:pu:U:6:?", + .long_options = longopts, +}; + +/* + * simulate PMDA busy (not responding to PDUs) + */ +int +limbo(void) +{ + __pmSendError(dispatch.version.two.ext->e_outfd, FROM_ANON, PM_ERR_PMDANOTREADY); + while (not_ready > 0) + not_ready = sleep(not_ready); + return PM_ERR_PMDAREADY; +} + +/* + * callback from pmdaMain + */ +static int +check(void) +{ + if (access("/tmp/sample.unavail", F_OK) == 0) + return PM_ERR_AGAIN; + return 0; +} + +/* + * callback from pmdaMain + */ +static void +done(void) +{ + if (sample_done) + exit(0); +} + +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + char helppath[MAXPATHLEN]; + char *username; + + _isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(helppath, sizeof(helppath), "%s%c" "sample" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_LATEST, pmProgname, SAMPLE, + "sample.log", helppath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + pmdaOpenLog(&dispatch); + if (opts.username) + username = opts.username; + __pmSetProcessIdentity(username); + + sample_init(&dispatch); + pmdaSetCheckCallBack(&dispatch, check); + pmdaSetDoneCallBack(&dispatch, done); + pmdaConnect(&dispatch); + +#ifdef HAVE_SIGHUP + /* + * Non-DSO agents should ignore gratuitous SIGHUPs, e.g. from a + * terminal when launched by the PCP Tutorial! + */ + signal(SIGHUP, SIG_IGN); +#endif + + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/sample/src/sample.c b/src/pmdas/sample/src/sample.c new file mode 100644 index 0000000..13a1328 --- /dev/null +++ b/src/pmdas/sample/src/sample.c @@ -0,0 +1,2748 @@ +/* + * Copyright (c) 2014 Red Hat. + * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include "percontext.h" +#include "events.h" +#include "domain.h" +#ifdef HAVE_SYSINFO +/* + * On Solaris, need and sysinfo() is different. + * Other platforms need + */ +#ifdef IS_SOLARIS +#include +#define MAX_SYSNAME 257 +#else +#include +#endif +#else +static struct sysinfo { + char dummy[64]; +} si = { { +'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', +'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', +'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', +'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '9', '[', ']', '.' +} }; +#endif + +static int need_mirage; /* only do mirage glop is someone asks for it */ +static int need_dynamic;/* only do dynamic glop is someone asks for it */ + +/* from pmda.c: simulate PMDA busy */ +extern int limbo(void); + +/* + * all metrics supported in this PMD - one table entry for each + */ + +static pmDesc desctab[] = { +/* control */ + { PMDA_PMID(0,0), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* daemon-pid */ + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* seconds */ + { PMDA_PMID(0,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, +/* milliseconds */ + { PMDA_PMID(0,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0) }, +/* load */ + { PMDA_PMID(0,4), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* colour */ + { PMDA_PMID(0,5), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* bin */ + { PMDA_PMID(0,6), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* drift */ + { PMDA_PMID(0,7), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* step */ + { PMDA_PMID(0,8), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* noinst */ + { PMDA_PMID(0,9), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.one */ + { PMDA_PMID(0,10), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.ten */ + { PMDA_PMID(0,11), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.hundred */ + { PMDA_PMID(0,12), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.million */ + { PMDA_PMID(0,13), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.write_me */ + { PMDA_PMID(0,14), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* float.one */ + { PMDA_PMID(0,15), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* float.ten */ + { PMDA_PMID(0,16), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* float.hundred */ + { PMDA_PMID(0,17), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* float.million */ + { PMDA_PMID(0,18), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* float.write_me */ + { PMDA_PMID(0,19), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* longlong.one */ + { PMDA_PMID(0,20), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* longlong.ten */ + { PMDA_PMID(0,21), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* longlong.hundred */ + { PMDA_PMID(0,22), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* longlong.million */ + { PMDA_PMID(0,23), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* longlong.write_me */ + { PMDA_PMID(0,24), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* double.one */ + { PMDA_PMID(0,25), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* double.ten */ + { PMDA_PMID(0,26), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* double.hundred */ + { PMDA_PMID(0,27), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* double.million */ + { PMDA_PMID(0,28), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* double.write_me */ + { PMDA_PMID(0,29), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* string.null */ + { PMDA_PMID(0,30), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* string.hullo */ + { PMDA_PMID(0,31), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* string.write_me */ + { PMDA_PMID(0,32), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* aggregate.null */ + { PMDA_PMID(0,33), PM_TYPE_AGGREGATE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* aggregate.hullo */ + { PMDA_PMID(0,34), PM_TYPE_AGGREGATE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* aggregate.write_me */ + { PMDA_PMID(0,35), PM_TYPE_AGGREGATE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* write_me */ + { PMDA_PMID(0,36), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE) }, +/* mirage */ + { PMDA_PMID(0,37), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0) }, +/* mirage-longlong */ + { PMDA_PMID(0,38), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_MSEC,0) }, +/* sysinfo */ + { PMDA_PMID(0,39), PM_TYPE_AGGREGATE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* pdu */ + { PMDA_PMID(0,40), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* recv-pdu */ + { PMDA_PMID(0,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* xmit-pdu */ + { PMDA_PMID(0,42), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* percontext.pdu */ + { PMDA_PMID(0,43), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* percontext.recv-pdu */ + { PMDA_PMID(0,44), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* percontext.xmit-pdu */ + { PMDA_PMID(0,45), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* lights */ + { PMDA_PMID(0,46), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* magnitude */ + { PMDA_PMID(0,47), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* bucket - alias for bin, but different PMID */ + { PMDA_PMID(0,48), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* needprofile - need explicit instance profile */ + { PMDA_PMID(0,49), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* part_bin - bin, minus an instance or two */ + { PMDA_PMID(0,50), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* bogus_bin - bin, plus an instance or two */ + { PMDA_PMID(0,51), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* hordes.one */ + { PMDA_PMID(0,52), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* hordes.two */ + { PMDA_PMID(0,53), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* bad.unknown */ + { PMDA_PMID(0,54), 0, 0, 0, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* bad.nosupport */ + { PMDA_PMID(0,55), PM_TYPE_NOSUPPORT, PM_INDOM_NULL, 0, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* not_ready */ + { PMDA_PMID(0,56), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* wrap.long */ + { PMDA_PMID(0,57), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* wrap.ulong */ + { PMDA_PMID(0,58), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* wrap.longlong */ + { PMDA_PMID(0,59), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* wrap.ulonglong */ + { PMDA_PMID(0,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* dodgey.control */ + { PMDA_PMID(0,61), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* dodgey.value */ + { PMDA_PMID(0,62), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* step_counter */ + { PMDA_PMID(0,63), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* rapid */ + { PMDA_PMID(0,64), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* scale_step.bytes_up */ + { PMDA_PMID(0,65), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) }, +/* scale_step.bytes_down */ + { PMDA_PMID(0,66), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, +/* scale_step.count_up */ + { PMDA_PMID(0,67), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE) }, +/* scale_step.count_down */ + { PMDA_PMID(0,68), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* scale_step.time_up_secs */ + { PMDA_PMID(0,69), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, +/* scale_step.time_up_nanosecs */ + { PMDA_PMID(0,70), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,1,0,0,PM_TIME_NSEC,0) }, +/* scale_step.none_up */ + { PMDA_PMID(0,71), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* const_rate.value */ + { PMDA_PMID(0,72), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* const_rate.gradient */ + { PMDA_PMID(0,73), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* error_code */ + { PMDA_PMID(0,74), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* error_check */ + { PMDA_PMID(0,75), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* dynamic.counter */ + { PMDA_PMID(0,76), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* dynamic.discrete */ + { PMDA_PMID(0,77), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* dynamic.instant */ + { PMDA_PMID(0,78), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* many.count */ + { PMDA_PMID(0,79), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,0) }, +/* many.int */ + { PMDA_PMID(0,80), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,0) }, +/* byte_ctr */ + { PMDA_PMID(0,81), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, +/* byte_rate */ + { PMDA_PMID(0,82), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) }, +/* kbyte_ctr */ + { PMDA_PMID(0,83), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* kbyte_rate */ + { PMDA_PMID(0,84), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0) }, +/* byte_rate_per_hour */ + { PMDA_PMID(0,85), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_HOUR,0) }, +/* dynamic.meta.metric - pmDesc here is a fake, use magic */ + { PMDA_PMID(0,86), 0, 0, 0, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* dynamic.meta.pmdesc.type */ + { PMDA_PMID(0,87), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* dynamic.meta.pmdesc.indom */ + { PMDA_PMID(0,88), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* dynamic.meta.pmdesc.sem */ + { PMDA_PMID(0,89), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* dynamic.meta.pmdesc.units */ + { PMDA_PMID(0,90), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* datasize */ + { PMDA_PMID(0,91), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* darkness */ + { PMDA_PMID(0,92), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulong.one */ + { PMDA_PMID(0,93), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulong.ten */ + { PMDA_PMID(0,94), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulong.hundred */ + { PMDA_PMID(0,95), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulong.million */ + { PMDA_PMID(0,96), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulong.write_me */ + { PMDA_PMID(0,97), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulonglong.one */ + { PMDA_PMID(0,98), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulonglong.ten */ + { PMDA_PMID(0,99), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulonglong.hundred */ + { PMDA_PMID(0,100), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulonglong.million */ + { PMDA_PMID(0,101), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulonglong.write_me */ + { PMDA_PMID(0,102), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.bin */ + { PMDA_PMID(0,103), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* long.bin_ctr */ + { PMDA_PMID(0,104), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* ulong.bin */ + { PMDA_PMID(0,105), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulong.bin_ctr */ + { PMDA_PMID(0,106), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* float.bin */ + { PMDA_PMID(0,107), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* float.bin_ctr */ + { PMDA_PMID(0,108), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* longlong.bin */ + { PMDA_PMID(0,109), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* longlong.bin_ctr */ + { PMDA_PMID(0,110), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* ulonglong.bin */ + { PMDA_PMID(0,111), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* ulonglong.bin_ctr */ + { PMDA_PMID(0,112), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* double.bin */ + { PMDA_PMID(0,113), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* double.bin_ctr */ + { PMDA_PMID(0,114), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER, PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, +/* sample.ulong.count.base */ + { PMDA_PMID(0,115), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(-1,0,1,PM_SPACE_MBYTE,0,PM_COUNT_ONE) }, +/* sample.ulong.count.deca */ + { PMDA_PMID(0,116), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(-1,0,1,PM_SPACE_MBYTE,0,PM_COUNT_ONE+1) }, +/* sample.ulong.count.hecto */ + { PMDA_PMID(0,117), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(-1,0,1,PM_SPACE_MBYTE,0,PM_COUNT_ONE+2) }, +/* sample.ulong.count.kilo */ + { PMDA_PMID(0,118), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(-1,0,1,PM_SPACE_MBYTE,0,PM_COUNT_ONE+3) }, +/* sample.ulong.count.mega */ + { PMDA_PMID(0,119), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(-1,0,1,PM_SPACE_MBYTE,0,PM_COUNT_ONE+6) }, +/* scramble.version */ + { PMDA_PMID(0,120), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* scramble.bin */ + { PMDA_PMID(0,121), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* percontext.control.ctx */ + { PMDA_PMID(0,122), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* percontext.control.active */ + { PMDA_PMID(0,123), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* percontext.control.start */ + { PMDA_PMID(0,124), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* percontext.control.end */ + { PMDA_PMID(0,125), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* event.reset */ + { PMDA_PMID(0,126), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.type */ + { PMDA_PMID(0,127), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_32 */ + { PMDA_PMID(0,128), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_u32 */ + { PMDA_PMID(0,129), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_64 */ + { PMDA_PMID(0,130), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_u64 */ + { PMDA_PMID(0,131), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_float */ + { PMDA_PMID(0,132), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_double */ + { PMDA_PMID(0,133), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_string */ + { PMDA_PMID(0,134), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.param_aggregate */ + { PMDA_PMID(0,135), PM_TYPE_AGGREGATE, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.records */ + { PMDA_PMID(0,136), PM_TYPE_EVENT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.no_indom_records */ + { PMDA_PMID(0,137), PM_TYPE_EVENT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* bad.novalues */ + { PMDA_PMID(0,138), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.highres_records */ + { PMDA_PMID(0,139), PM_TYPE_HIGHRES_EVENT, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* event.reset */ + { PMDA_PMID(0,140), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, + +/* + * dynamic PMNS ones + * secret.bar + */ + { PMDA_PMID(0,1000), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,0,0,0,0) }, +/* secret.foo.one */ + { PMDA_PMID(0,1001), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* secret.foo.two */ + { PMDA_PMID(0,1002), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* secret.foo.bar.three */ + { PMDA_PMID(0,1003), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* secret.foo.bar.four */ + { PMDA_PMID(0,1004), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* secret.foo.bar.grunt.five */ + { PMDA_PMID(0,1005), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* secret.foo.bar.grunt.snort.six */ + { PMDA_PMID(0,1006), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, +/* secret.foo.bar.grunt.snort.seven */ + { PMDA_PMID(0,1007), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, + +/* bigid */ + { PMDA_PMID(0,1023), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, + +/* End-of-List */ + { PM_ID_NULL, 0, 0, 0, { 0, 0, 0, 0, 0, 0 } } +}; +static int direct_map = 1; +static int ndesc = sizeof(desctab)/sizeof(desctab[0]); + +static pmDesc magic = + { PMDA_PMID(0,86), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, PMDA_PMUNITS(1,-1,0,PM_SPACE_BYTE,PM_TIME_SEC,0) }; + +static pmdaInstid _colour[] = { + { 0, "red" }, { 1, "green" }, { 2, "blue" } +}; + +static pmdaInstid _bin[] = { + { 100, "bin-100" }, { 200, "bin-200" }, { 300, "bin-300" }, + { 400, "bin-400" }, { 500, "bin-500" }, { 600, "bin-600" }, + { 700, "bin-700" }, { 800, "bin-800" }, { 900, "bin-900" } +}; + +static pmdaInstid _scramble[] = { + { 100, "bin-100" }, { 200, "bin-200" }, { 300, "bin-300" }, + { 400, "bin-400" }, { 500, "bin-500" }, { 600, "bin-600" }, + { 700, "bin-700" }, { 800, "bin-800" }, { 900, "bin-900" } +}; + +static long scramble_ver = 0; + +static pmdaInstid _family[] = { + { 0, "colleen" }, { 1, "terry" }, { 2, "emma" }, { 3, "cathy" }, { 4, "fat bald bastard" } +}; + +static pmdaInstid _dodgey[] = { + { 1, NULL}, { 2, NULL }, { 3, NULL }, { 4, NULL }, { 5, NULL } +}; + +static pmdaInstid _hordes[] = { + { 0, "0" }, { 1, "1" }, { 2, "2" }, { 3, "3" }, { 4, "4" }, + { 5, "5" }, { 6, "6" }, { 7, "7" }, { 8, "8" }, { 9, "9" }, + { 10, "10" }, { 11, "11" }, { 12, "12" }, { 13, "13" }, { 14, "14" }, + { 15, "15" }, { 16, "16" }, { 17, "17" }, { 18, "18" }, { 19, "19" }, + { 20, "20" }, { 21, "21" }, { 22, "22" }, { 23, "23" }, { 24, "24" }, + { 25, "25" }, { 26, "26" }, { 27, "27" }, { 28, "28" }, { 29, "29" }, + { 30, "30" }, { 31, "31" }, { 32, "32" }, { 33, "33" }, { 34, "34" }, + { 35, "35" }, { 36, "36" }, { 37, "37" }, { 38, "38" }, { 39, "39" }, + { 40, "40" }, { 41, "41" }, { 42, "42" }, { 43, "43" }, { 44, "44" }, + { 45, "45" }, { 46, "46" }, { 47, "47" }, { 48, "48" }, { 49, "49" }, + { 50, "50" }, { 51, "51" }, { 52, "52" }, { 53, "53" }, { 54, "54" }, + { 55, "55" }, { 56, "56" }, { 57, "57" }, { 58, "58" }, { 59, "59" }, + { 60, "60" }, { 61, "61" }, { 62, "62" }, { 63, "63" }, { 64, "64" }, + { 65, "65" }, { 66, "66" }, { 67, "67" }, { 68, "68" }, { 69, "69" }, + { 70, "70" }, { 71, "71" }, { 72, "72" }, { 73, "73" }, { 74, "74" }, + { 75, "75" }, { 76, "76" }, { 77, "77" }, { 78, "78" }, { 79, "79" }, + { 80, "80" }, { 81, "81" }, { 82, "82" }, { 83, "83" }, { 84, "84" }, + { 85, "85" }, { 86, "86" }, { 87, "87" }, { 88, "88" }, { 89, "89" }, + { 90, "90" }, { 91, "91" }, { 92, "92" }, { 93, "93" }, { 94, "94" }, + { 95, "95" }, { 96, "96" }, { 97, "97" }, { 98, "98" }, { 99, "99" }, + {100, "100" }, {101, "101" }, {102, "102" }, {103, "103" }, {104, "104" }, + {105, "105" }, {106, "106" }, {107, "107" }, {108, "108" }, {109, "109" }, + {110, "110" }, {111, "111" }, {112, "112" }, {113, "113" }, {114, "114" }, + {115, "115" }, {116, "116" }, {117, "117" }, {118, "118" }, {119, "119" }, + {120, "120" }, {121, "121" }, {122, "122" }, {123, "123" }, {124, "124" }, + {125, "125" }, {126, "126" }, {127, "127" }, {128, "128" }, {129, "129" }, + {130, "130" }, {131, "131" }, {132, "132" }, {133, "133" }, {134, "134" }, + {135, "135" }, {136, "136" }, {137, "137" }, {138, "138" }, {139, "139" }, + {140, "140" }, {141, "141" }, {142, "142" }, {143, "143" }, {144, "144" }, + {145, "145" }, {146, "146" }, {147, "147" }, {148, "148" }, {149, "149" }, + {150, "150" }, {151, "151" }, {152, "152" }, {153, "153" }, {154, "154" }, + {155, "155" }, {156, "156" }, {157, "157" }, {158, "158" }, {159, "159" }, + {160, "160" }, {161, "161" }, {162, "162" }, {163, "163" }, {164, "164" }, + {165, "165" }, {166, "166" }, {167, "167" }, {168, "168" }, {169, "169" }, + {170, "170" }, {171, "171" }, {172, "172" }, {173, "173" }, {174, "174" }, + {175, "175" }, {176, "176" }, {177, "177" }, {178, "178" }, {179, "179" }, + {180, "180" }, {181, "181" }, {182, "182" }, {183, "183" }, {184, "184" }, + {185, "185" }, {186, "186" }, {187, "187" }, {188, "188" }, {189, "189" }, + {190, "190" }, {191, "191" }, {192, "192" }, {193, "193" }, {194, "194" }, + {195, "195" }, {196, "196" }, {197, "197" }, {198, "198" }, {199, "199" }, + {200, "200" }, {201, "201" }, {202, "202" }, {203, "203" }, {204, "204" }, + {205, "205" }, {206, "206" }, {207, "207" }, {208, "208" }, {209, "209" }, + {210, "210" }, {211, "211" }, {212, "212" }, {213, "213" }, {214, "214" }, + {215, "215" }, {216, "216" }, {217, "217" }, {218, "218" }, {219, "219" }, + {220, "220" }, {221, "221" }, {222, "222" }, {223, "223" }, {224, "224" }, + {225, "225" }, {226, "226" }, {227, "227" }, {228, "228" }, {229, "229" }, + {230, "230" }, {231, "231" }, {232, "232" }, {233, "233" }, {234, "234" }, + {235, "235" }, {236, "236" }, {237, "237" }, {238, "238" }, {239, "239" }, + {240, "240" }, {241, "241" }, {242, "242" }, {243, "243" }, {244, "244" }, + {245, "245" }, {246, "246" }, {247, "247" }, {248, "248" }, {249, "249" }, + {250, "250" }, {251, "251" }, {252, "252" }, {253, "253" }, {254, "254" }, + {255, "255" }, {256, "256" }, {257, "257" }, {258, "258" }, {259, "259" }, + {260, "260" }, {261, "261" }, {262, "262" }, {263, "263" }, {264, "264" }, + {265, "265" }, {266, "266" }, {267, "267" }, {268, "268" }, {269, "269" }, + {270, "270" }, {271, "271" }, {272, "272" }, {273, "273" }, {274, "274" }, + {275, "275" }, {276, "276" }, {277, "277" }, {278, "278" }, {279, "279" }, + {280, "280" }, {281, "281" }, {282, "282" }, {283, "283" }, {284, "284" }, + {285, "285" }, {286, "286" }, {287, "287" }, {288, "288" }, {289, "289" }, + {290, "290" }, {291, "291" }, {292, "292" }, {293, "293" }, {294, "294" }, + {295, "295" }, {296, "296" }, {297, "297" }, {298, "298" }, {299, "299" }, + {300, "300" }, {301, "301" }, {302, "302" }, {303, "303" }, {304, "304" }, + {305, "305" }, {306, "306" }, {307, "307" }, {308, "308" }, {309, "309" }, + {310, "310" }, {311, "311" }, {312, "312" }, {313, "313" }, {314, "314" }, + {315, "315" }, {316, "316" }, {317, "317" }, {318, "318" }, {319, "319" }, + {320, "320" }, {321, "321" }, {322, "322" }, {323, "323" }, {324, "324" }, + {325, "325" }, {326, "326" }, {327, "327" }, {328, "328" }, {329, "329" }, + {330, "330" }, {331, "331" }, {332, "332" }, {333, "333" }, {334, "334" }, + {335, "335" }, {336, "336" }, {337, "337" }, {338, "338" }, {339, "339" }, + {340, "340" }, {341, "341" }, {342, "342" }, {343, "343" }, {344, "344" }, + {345, "345" }, {346, "346" }, {347, "347" }, {348, "348" }, {349, "349" }, + {350, "350" }, {351, "351" }, {352, "352" }, {353, "353" }, {354, "354" }, + {355, "355" }, {356, "356" }, {357, "357" }, {358, "358" }, {359, "359" }, + {360, "360" }, {361, "361" }, {362, "362" }, {363, "363" }, {364, "364" }, + {365, "365" }, {366, "366" }, {367, "367" }, {368, "368" }, {369, "369" }, + {370, "370" }, {371, "371" }, {372, "372" }, {373, "373" }, {374, "374" }, + {375, "375" }, {376, "376" }, {377, "377" }, {378, "378" }, {379, "379" }, + {380, "380" }, {381, "381" }, {382, "382" }, {383, "383" }, {384, "384" }, + {385, "385" }, {386, "386" }, {387, "387" }, {388, "388" }, {389, "389" }, + {390, "390" }, {391, "391" }, {392, "392" }, {393, "393" }, {394, "394" }, + {395, "395" }, {396, "396" }, {397, "397" }, {398, "398" }, {399, "399" }, + {400, "400" }, {401, "401" }, {402, "402" }, {403, "403" }, {404, "404" }, + {405, "405" }, {406, "406" }, {407, "407" }, {408, "408" }, {409, "409" }, + {410, "410" }, {411, "411" }, {412, "412" }, {413, "413" }, {414, "414" }, + {415, "415" }, {416, "416" }, {417, "417" }, {418, "418" }, {419, "419" }, + {420, "420" }, {421, "421" }, {422, "422" }, {423, "423" }, {424, "424" }, + {425, "425" }, {426, "426" }, {427, "427" }, {428, "428" }, {429, "429" }, + {430, "430" }, {431, "431" }, {432, "432" }, {433, "433" }, {434, "434" }, + {435, "435" }, {436, "436" }, {437, "437" }, {438, "438" }, {439, "439" }, + {440, "440" }, {441, "441" }, {442, "442" }, {443, "443" }, {444, "444" }, + {445, "445" }, {446, "446" }, {447, "447" }, {448, "448" }, {449, "449" }, + {450, "450" }, {451, "451" }, {452, "452" }, {453, "453" }, {454, "454" }, + {455, "455" }, {456, "456" }, {457, "457" }, {458, "458" }, {459, "459" }, + {460, "460" }, {461, "461" }, {462, "462" }, {463, "463" }, {464, "464" }, + {465, "465" }, {466, "466" }, {467, "467" }, {468, "468" }, {469, "469" }, + {470, "470" }, {471, "471" }, {472, "472" }, {473, "473" }, {474, "474" }, + {475, "475" }, {476, "476" }, {477, "477" }, {478, "478" }, {479, "479" }, + {480, "480" }, {481, "481" }, {482, "482" }, {483, "483" }, {484, "484" }, + {485, "485" }, {486, "486" }, {487, "487" }, {488, "488" }, {489, "489" }, + {490, "490" }, {491, "491" }, {492, "492" }, {493, "493" }, {494, "494" }, + {495, "495" }, {496, "496" }, {497, "497" }, {498, "498" }, {499, "499" } +}; + +static pmdaInstid _events[] = { + { 0, "fungus" }, { 1, "bogus" } +}; + +/* all domains supported in this PMDA - one entry each */ +static pmdaIndom indomtab[] = { +#define COLOUR_INDOM 0 + { 0, 3, _colour }, +#define BIN_INDOM 1 + { 0, 9, _bin }, +#define MIRAGE_INDOM 2 + { 0, 0, NULL }, +#define FAMILY_INDOM 3 + { 0, 5, _family }, +#define HORDES_INDOM 4 + { 0, 500, _hordes }, +#define DODGEY_INDOM 5 + { 0, 5, _dodgey }, +#define DYNAMIC_INDOM 6 + { 0, 0, NULL }, +#define MANY_INDOM 7 + { 0, 5, NULL }, +#define SCRAMBLE_INDOM 8 + { 0, 9, _scramble }, +#define EVENT_INDOM 9 + { 0, 2, _events }, + + { PM_INDOM_NULL, 0, 0 } +}; + +static struct timeval _then; /* time we started */ +static time_t _start; /* ditto */ +static __pmProfile *_profile; /* last received profile */ +static int _x; +static pmdaIndom *_idp; +static int _singular = -1; /* =0 for singular values */ +static int _ordinal = -1; /* >=0 for non-singular values */ +static int _control; /* the control variable */ +static int _mypid; +static int _drift = 200; /* starting value for drift */ +static int _sign = -1; /* up/down for drift */ +static int _step = 20; /* magnitude of step */ +static int _write_me = 2; /* constant, but modifiable */ +static __int32_t _long = 13; /* long.write_me */ +static __uint32_t _ulong = 13; /* ulong.write_me */ +static __int64_t _longlong = 13; /* longlong.write_me */ +static __uint64_t _ulonglong = 13;/* ulonglong.write_me */ +static float _float = 13; /* float.write_me */ +static double _double = 13; /* double.write_me */ +static char *_string; /* string.write_me */ +static pmValueBlock *_aggr33; /* aggregate.null */ +static pmValueBlock *_aggr34; /* aggregate.hullo */ +static pmValueBlock *_aggr35; /* aggregate.write_me */ +static long _col46; /* lights */ +static int _n46; /* sample count for lights */ +static long _mag47; /* magnitude */ +static int _n47; /* sample count for magnitude */ +static __uint32_t _rapid; /* counts @ 8x10^8 per fetch */ +static int _dyn_max = -1; +static int *_dyn_ctr; +static int many_count = 5; + +static pmValueBlock *sivb=NULL; + +static __int32_t _wrap = 0; /* wrap.long */ +static __uint32_t _u_wrap = 0; /* wrap.ulong */ +static __int64_t _ll_wrap = 0; /* wrap.longlong */ +static __uint64_t _ull_wrap = 0; /* wrap.ulonglong */ + +static int _error_code = 0;/* return this! */ + +static int dodgey = 5; /* dodgey.control */ +static int tmp_dodgey = 5; +static int new_dodgey = 0; + +static double scale_step_bytes_up = 1; +static double scale_step_bytes_down = 1; +static double scale_step_count_up = 1; +static double scale_step_count_down = 1; +static double scale_step_time_up_secs = 1; +static double scale_step_time_up_nanosecs = 1; +static double scale_step_none_up = 1; +static int scale_step_number[7] = {0,0,0,0,0,0,0}; + +static __uint32_t const_rate_gradient = 0; +static __uint32_t const_rate_value = 10485760; +static struct timeval const_rate_timestamp = {0,0}; + +/* this needs to be visible in pmda.c */ +int not_ready = 0; /* sleep interval in seconds */ +int sample_done = 0;/* pending request to terminate, see sample_store() */ + +int _isDSO = 1; /* =0 I am a daemon */ + +/* + * dynamic PMNS metrics ... nothing to do with redo_dynamic() and dynamic + * InDoms + */ +static struct { + char *name; + pmID pmid; + int mark; +} dynamic_ones[] = { + { "secret.foo.bar.max.redirect", PMDA_PMID(0,0) }, + { "secret.bar", PMDA_PMID(0,1000) }, + { "secret.foo.one", PMDA_PMID(0,1001) }, + { "secret.foo.two", PMDA_PMID(0,1002) }, + { "secret.foo.bar.three", PMDA_PMID(0,1003) }, + { "secret.foo.bar.four", PMDA_PMID(0,1004) }, + { "secret.foo.bar.grunt.five", PMDA_PMID(0,1005) }, + { "secret.foo.bar.grunt.snort.six", PMDA_PMID(0,1006) }, + { "secret.foo.bar.grunt.snort.huff.puff.seven", PMDA_PMID(0,1007) } +}; +static int numdyn = sizeof(dynamic_ones)/sizeof(dynamic_ones[0]); + +static int +redo_dynamic(void) +{ + int err; + int i; + int sep = __pmPathSeparator(); + static struct stat lastsbuf; + struct stat statbuf; + pmdaIndom *idp = &indomtab[DYNAMIC_INDOM]; + char mypath[MAXPATHLEN]; + + snprintf(mypath, sizeof(mypath), "%s%c" "sample" "%c" "dynamic.indom", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + + if (stat(mypath, &statbuf) == 0) { +#if defined(HAVE_ST_MTIME_WITH_E) && defined(HAVE_STAT_TIME_T) + if (statbuf.st_mtime != lastsbuf.st_mtime) +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + if ((statbuf.st_mtimespec.tv_sec != lastsbuf.st_mtimespec.tv_sec) || + (statbuf.st_mtimespec.tv_nsec != lastsbuf.st_mtimespec.tv_nsec)) +#elif defined(HAVE_STAT_TIMESTRUC) || defined(HAVE_STAT_TIMESPEC) || defined(HAVE_STAT_TIMESPEC_T) + if ((statbuf.st_mtim.tv_sec != lastsbuf.st_mtim.tv_sec) || + (statbuf.st_mtim.tv_nsec != lastsbuf.st_mtim.tv_nsec)) +#else +!bozo! +#endif + { + FILE *fspec; + int newinst; + char newname[100]; /* hack, secret max */ + int numinst; + + lastsbuf = statbuf; + if ((fspec = fopen(mypath, "r")) != NULL) { + for (i = 0; i < idp->it_numinst; i++) { + free(idp->it_set[i].i_name); + } + for (i = 0; i <= _dyn_max; i++) { + _dyn_ctr[i] = -_dyn_ctr[i]; + } + free(idp->it_set); + idp->it_numinst = 0; + idp->it_set = NULL; + numinst = 0; + for ( ; ; ) { + if (fscanf(fspec, "%d %s", &newinst, newname) != 2) + break; + numinst++; + if ((idp->it_set = (pmdaInstid *)realloc(idp->it_set, numinst * sizeof(pmdaInstid))) == NULL) { + err = -oserror(); + fclose(fspec); + return err; + } + idp->it_set[numinst-1].i_inst = newinst; + if ((idp->it_set[numinst-1].i_name = strdup(newname)) == NULL) { + err = -oserror(); + free(idp->it_set); + idp->it_set = NULL; + fclose(fspec); + return err; + } + if (newinst > _dyn_max) { + if ((_dyn_ctr = (int *)realloc(_dyn_ctr, (newinst+1)*sizeof(_dyn_ctr[0]))) == NULL) { + err = -oserror(); + free(idp->it_set); + idp->it_set = NULL; + fclose(fspec); + return err; + } + for (i = _dyn_max+1; i <= newinst; i++) + _dyn_ctr[i] = 0; + _dyn_max = newinst; + } + _dyn_ctr[newinst] = -_dyn_ctr[newinst]; + } + fclose(fspec); + idp->it_numinst = numinst; + + for (i = 0; i <= _dyn_max; i++) { + if (_dyn_ctr[i] < 0) + _dyn_ctr[i] = 0; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "redo instance domain for dynamic: numinst: %d\n", idp->it_numinst); + for (i = 0; i < idp->it_numinst; i++) { + fprintf(stderr, " %d \"%s\"", idp->it_set[i].i_inst, idp->it_set[i].i_name); + } + fputc('\n', stderr); + } +#endif + } + } + } + else { + /* control file is not present, empty indom if not already so */ + if (idp->it_set != NULL) { + for (i = 0; i < idp->it_numinst; i++) { + free(idp->it_set[i].i_name); + } + free(idp->it_set); + idp->it_set = NULL; + idp->it_numinst = 0; + for (i = 0; i <= _dyn_max; i++) { + _dyn_ctr[i] = 0; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "redo instance domain for dynamic: numinst: 0 (no control file)\n"); +#endif + } + } + + for (i = 0; i < idp->it_numinst; i++) { + _dyn_ctr[idp->it_set[i].i_inst]++; + } + + return 0; +} + +#define MANY_MAX_LEN 10 + +static int +redo_many(void) +{ + pmdaIndom *idp; + int a; + static char *tags=NULL; + char *tag; + + /* sanity check, range clip */ + + if (many_count<0) many_count=0; + if (many_count>999999) many_count=999999; + + idp = &indomtab[MANY_INDOM]; + + /* realloc instances buffer */ + + idp->it_set = realloc(idp->it_set, many_count*sizeof(pmdaInstid)); + if (!idp->it_set) { + idp->it_numinst=0; + many_count=0; + return -oserror(); + } + + /* realloc string buffer */ + + tags = realloc(tags, many_count*MANY_MAX_LEN); + if (!idp->it_set) { + idp->it_numinst=0; + many_count=0; + return -oserror(); + } + + /* set number of instances */ + + idp->it_numinst=many_count; + + /* generate instances */ + + tag=tags; + for (a=0;ait_set[a].i_inst=a; + idp->it_set[a].i_name=tag; + tag+=sprintf(tag,"i-%d",a)+1; + } + + return 0; +} + +static int +redo_mirage(void) +{ + static time_t doit = 0; + time_t now; + int i; + int j; + static int newinst = 0; + pmdaIndom *idp; + + now = time(NULL); + if (now < doit) + return 0; + + idp = &indomtab[MIRAGE_INDOM]; + if (idp->it_set == NULL) { + /* first time */ + if ((idp->it_set = (pmdaInstid *)malloc(sizeof(pmdaInstid))) == NULL) + return -oserror(); + if ((idp->it_set[0].i_name = (char *)malloc(5)) == NULL) { + idp->it_set = NULL; + return -oserror(); + } + idp->it_numinst = 1; + idp->it_set[0].i_inst = 0; + sprintf(idp->it_set[0].i_name, "m-%02d", 0); + } + else { + int numinst; + int cull; + + numinst = 1; + cull = idp->it_numinst > 12 ? idp->it_numinst/2 : idp->it_numinst; + for (i = 1; i < idp->it_numinst; i++) { + if (lrand48() % 1000 < 1000 / cull) { + /* delete this one */ + free(idp->it_set[i].i_name); + continue; + } + idp->it_set[numinst++] = idp->it_set[i]; + } + if (numinst != idp->it_numinst) { + if ((idp->it_set = (pmdaInstid *)realloc(idp->it_set, numinst * sizeof(pmdaInstid))) == NULL) { + idp->it_set = NULL; + idp->it_numinst = 0; + return -oserror(); + } + idp->it_numinst = numinst; + } + for (i = 0; i < 2; i++) { + if (lrand48() % 1000 < 500) { + /* add a new one */ + numinst++; + if ((idp->it_set = (pmdaInstid *)realloc(idp->it_set, numinst * sizeof(pmdaInstid))) == NULL) { + idp->it_set = NULL; + idp->it_numinst = 0; + return -oserror(); + } + if ((idp->it_set[numinst-1].i_name = (char *)malloc(5)) == NULL) { + idp->it_set = NULL; + return -oserror(); + } + for ( ; ; ) { + newinst = (newinst + 1) % 50; + for (j = 0; j < idp->it_numinst; j++) { + if (idp->it_set[j].i_inst == newinst) + break; + } + if (j == idp->it_numinst) + break; + } + idp->it_numinst = numinst; + idp->it_set[numinst-1].i_inst = newinst; + sprintf(idp->it_set[numinst-1].i_name, "m-%02d", newinst); + } + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "redo instance domain for mirage: numinst: %d\n", idp->it_numinst); + for (i = 0; i < idp->it_numinst; i++) { + fprintf(stderr, " %d \"%s\"", idp->it_set[i].i_inst, idp->it_set[i].i_name); + } + fputc('\n', stderr); + } +#endif + + doit = now + 10; /* no more than once every 10 seconds */ + return 0; +} + +static void +redo_dodgey(void) +{ + int j; + int k; + + if (dodgey <= 5) { + tmp_dodgey = dodgey; + new_dodgey = 0; + /* re-build full instance table */ + for (j = 0; j < 5; j++) { + _dodgey[j].i_inst = j+1; + _dodgey[j].i_name[1] = '0' + j+1; + } + indomtab[DODGEY_INDOM].it_numinst = 5; + } + else { + j = (int)(lrand48() % 1000); + if (j < 33) + tmp_dodgey = PM_ERR_NOAGENT; + else if (j < 66) + tmp_dodgey = PM_ERR_AGAIN; + else if (j < 99) + tmp_dodgey = PM_ERR_APPVERSION; + else { + /* + * create partial instance table, instances appear + * at random with prob = 0.5 + */ + k = 0; + for (j = 0; j < 5; j++) { + if (lrand48() % 100 < 49) { + _dodgey[k].i_inst = j+1; + _dodgey[k].i_name[1] = '0' + j+1; + k++; + } + } + tmp_dodgey = indomtab[DODGEY_INDOM].it_numinst = k; + } + /* fetches before re-setting */ + new_dodgey = (int)(lrand48() % dodgey); + } +} + +/* + * count the number of instances in an instance domain + */ +static int +cntinst(pmInDom indom) +{ + pmdaIndom *idp; + + if (indom == PM_INDOM_NULL) + return 1; + for (idp = indomtab; idp->it_indom != PM_INDOM_NULL; idp++) { + if (idp->it_indom == indom) + return idp->it_numinst; + } + __pmNotifyErr(LOG_WARNING, "cntinst: unknown pmInDom 0x%x", indom); + return 0; +} + +/* + * commence a new round of instance selection + * flag == 1 for prefetch instance counting + * flag == 0 for iteration over instances to retrieve values + */ +static void +startinst(pmInDom indom, int flag) +{ + _ordinal = _singular = -1; + if (indom == PM_INDOM_NULL) { + /* singular value */ + _singular = 0; + return; + } + for (_idp = indomtab; _idp->it_indom != PM_INDOM_NULL; _idp++) { + if (_idp->it_indom == indom) { + /* multiple values are possible */ + _ordinal = 0; + if (flag == 1 && _idp == &indomtab[SCRAMBLE_INDOM]) { + /* + * indomtab[BIN_INDOM].it_set[] is the same size as + * indomtab[SCRAMBLE_INDOM].it_set[] (maxnuminst + * entries) + */ + int i; + int k = 0; + int maxnuminst = indomtab[BIN_INDOM].it_numinst; + srand48((scramble_ver << 10) + 13); + scramble_ver++; + for (i = 0; i < maxnuminst; i++) + indomtab[SCRAMBLE_INDOM].it_set[i].i_inst = PM_IN_NULL; + for (i = 0; i < maxnuminst; i++) { + /* skip 1/3 of instances */ + if ((lrand48() % 100) < 33) continue; + /* order of instances is random */ + for ( ; ; ) { + k = lrand48() % maxnuminst; + if (indomtab[SCRAMBLE_INDOM].it_set[k].i_inst != PM_IN_NULL) + continue; + indomtab[SCRAMBLE_INDOM].it_set[k].i_inst = indomtab[BIN_INDOM].it_set[i].i_inst; + indomtab[SCRAMBLE_INDOM].it_set[k].i_name = indomtab[BIN_INDOM].it_set[i].i_name; + break; + } + } + /* pack to remove skipped instances */ + k = 0; + for (i = 0; i < maxnuminst; i++) { + if (indomtab[SCRAMBLE_INDOM].it_set[i].i_inst == PM_IN_NULL) + continue; + if (k < i) { + indomtab[SCRAMBLE_INDOM].it_set[k].i_inst = indomtab[SCRAMBLE_INDOM].it_set[i].i_inst; + indomtab[SCRAMBLE_INDOM].it_set[k].i_name = indomtab[SCRAMBLE_INDOM].it_set[i].i_name; + } + k++; + } + indomtab[SCRAMBLE_INDOM].it_numinst = k; + } + break; + } + } +} + +/* + * find next selected instance, if any + * + * EXCEPTION PCP 2.1.1: make use of __pmProfile much smarter, particularly when state for + * this indom is PM_PROFILE_EXCLUDE, then only need to consider inst + * values in the profile - this is a performance enhancement, and + * the simple method is functionally complete, particularly for + * stable (non-varying) instance domains + */ +static int +nextinst(int *inst) +{ + int j; + + if (_singular == 0) { + /* PM_INDOM_NULL ... just the one value */ + *inst = 0; + _singular = -1; + return 1; + } + if (_ordinal >= 0) { + /* scan for next value in the profile */ + for (j = _ordinal; j < _idp->it_numinst; j++) { + if (__pmInProfile(_idp->it_indom, _profile, _idp->it_set[j].i_inst)) { + *inst = _idp->it_set[j].i_inst; + _ordinal = j+1; + return 1; + } + } + _ordinal = -1; + } + return 0; +} + +/* + * this routine is called at initialization to patch up any parts of the + * desctab that cannot be statically initialized, and to optionally + * modify our Performance Metrics Domain Id (dom) + */ +static void +init_tables(int dom) +{ + int i; + __pmInDom_int b_indom; + __pmInDom_int *indomp; + __pmID_int *pmidp; + pmDesc *dp; + + /* serial numbering is arbitrary, but must be unique in this PMD */ + b_indom.flag = 0; + b_indom.domain = dom; + b_indom.serial = 1; + indomp = (__pmInDom_int *)&indomtab[COLOUR_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[BIN_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[MIRAGE_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[FAMILY_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[HORDES_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[DODGEY_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[DYNAMIC_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[MANY_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[SCRAMBLE_INDOM].it_indom; + *indomp = b_indom; + b_indom.serial++; + indomp = (__pmInDom_int *)&indomtab[EVENT_INDOM].it_indom; + *indomp = b_indom; + + /* rewrite indom in desctab[] */ + for (dp = desctab; dp->pmid != PM_ID_NULL; dp++) { + switch (dp->pmid) { + case PMDA_PMID(0,5): /* colour */ + case PMDA_PMID(0,92): /* darkness */ + dp->indom = indomtab[COLOUR_INDOM].it_indom; + break; + case PMDA_PMID(0,6): /* bin */ + case PMDA_PMID(0,48): /* bucket */ + case PMDA_PMID(0,50): /* part_bin */ + case PMDA_PMID(0,51): /* bogus_bin */ + case PMDA_PMID(0,103): /* long.bin */ + case PMDA_PMID(0,104): /* long.bin_ctr */ + case PMDA_PMID(0,105): /* ulong.bin */ + case PMDA_PMID(0,106): /* ulong.bin_ctr */ + case PMDA_PMID(0,107): /* float.bin */ + case PMDA_PMID(0,108): /* float.bin_ctr */ + case PMDA_PMID(0,109): /* longlong.bin */ + case PMDA_PMID(0,110): /* longlong.bin_ctr */ + case PMDA_PMID(0,111): /* ulonglong.bin */ + case PMDA_PMID(0,112): /* ulonglong.bin_ctr */ + case PMDA_PMID(0,113): /* double.bin */ + case PMDA_PMID(0,114): /* double.bin_ctr */ + dp->indom = indomtab[BIN_INDOM].it_indom; + break; + case PMDA_PMID(0,37): /* mirage */ + dp->indom = indomtab[MIRAGE_INDOM].it_indom; + break; + case PMDA_PMID(0,38): /* mirage-longlong */ + dp->indom = indomtab[MIRAGE_INDOM].it_indom; + break; + case PMDA_PMID(0,49): /* needprofile */ + dp->indom = indomtab[FAMILY_INDOM].it_indom; + break; + case PMDA_PMID(0,52): /* hordes.one */ + case PMDA_PMID(0,53): /* hordes.two */ + dp->indom = indomtab[HORDES_INDOM].it_indom; + break; + case PMDA_PMID(0,62): /* dodgey.value */ + dp->indom = indomtab[DODGEY_INDOM].it_indom; + break; + case PMDA_PMID(0,76): /* dynamic.counter */ + case PMDA_PMID(0,77): /* dynamic.discrete */ + case PMDA_PMID(0,78): /* dynamic.instant */ + dp->indom = indomtab[DYNAMIC_INDOM].it_indom; + break; + case PMDA_PMID(0,80): /* many.int */ + dp->indom = indomtab[MANY_INDOM].it_indom; + break; + case PMDA_PMID(0,121): /* scramble.bin */ + dp->indom = indomtab[SCRAMBLE_INDOM].it_indom; + break; + case PMDA_PMID(0,136): /* event.records */ + case PMDA_PMID(0,139): /* event.highres_records */ + dp->indom = indomtab[EVENT_INDOM].it_indom; + break; + } + } + + /* merge performance domain id part into PMIDs in pmDesc table */ + for (i = 0; desctab[i].pmid != PM_ID_NULL; i++) { + pmidp = (__pmID_int *)&desctab[i].pmid; + pmidp->domain = dom; + if (direct_map && pmidp->item != i) { + direct_map = 0; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmNotifyErr(LOG_WARNING, "sample_init: direct map disabled @ desctab[%d]", i); + } +#endif + } + } + ndesc--; + pmidp = (__pmID_int *)&magic.pmid; + pmidp->domain = dom; + + /* local hacks */ + _string = (char *)calloc(1, 8); + strncpy(_string, "13", sizeof("13")); + _aggr33 = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE); + _aggr33->vlen = PM_VAL_HDR_SIZE + 0; + _aggr33->vtype = PM_TYPE_AGGREGATE; + _aggr34 = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE+strlen("hullo world!")); + _aggr34->vlen = PM_VAL_HDR_SIZE + strlen("hullo world!"); + _aggr34->vtype = PM_TYPE_AGGREGATE; + memcpy(_aggr34->vbuf, "hullo world!", strlen("hullo world!")); + _aggr35 = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE+strlen("13")); + _aggr35->vlen = PM_VAL_HDR_SIZE + strlen("13"); + _aggr35->vtype = PM_TYPE_AGGREGATE; + memcpy(_aggr35->vbuf, "13", strlen("13")); + + (void)redo_many(); +} + +static int +sample_profile(__pmProfile *prof, pmdaExt *ep) +{ + sample_inc_recv(ep->e_context); + _profile = prof; + return 0; +} + +static int +sample_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *ep) +{ + int i; + __pmInResult *res; + pmdaIndom *idp; + int err = 0; + + sample_inc_recv(ep->e_context); + sample_inc_xmit(ep->e_context); + + if (not_ready > 0) { + return limbo(); + } + + if (need_mirage && (i = redo_mirage()) < 0) + return i; + if (need_dynamic && (i = redo_dynamic()) < 0) + return i; + + /* + * check this is an instance domain we know about -- code below + * assumes this test is complete + */ + for (idp = indomtab; idp->it_indom != PM_INDOM_NULL; idp++) { + if (idp->it_indom == indom) + break; + } + if (idp->it_indom == PM_INDOM_NULL) + return PM_ERR_INDOM; + + if ((res = (__pmInResult *)malloc(sizeof(*res))) == NULL) + return -oserror(); + res->indom = indom; + + if (name == NULL && inst == PM_IN_NULL) + res->numinst = cntinst(indom); + else + res->numinst = 1; + + if (inst == PM_IN_NULL) { + if ((res->instlist = (int *)malloc(res->numinst * sizeof(res->instlist[0]))) == NULL) { + free(res); + return -oserror(); + } + } + else + res->instlist = NULL; + + if (name == NULL) { + if ((res->namelist = (char **)malloc(res->numinst * sizeof(res->namelist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + for (i = 0; i < res->numinst; i++) + res->namelist[0] = NULL; + } + else + res->namelist = NULL; + + if (name == NULL && inst == PM_IN_NULL) { + /* return inst and name for everything */ + for (i = 0; i < res->numinst; i++) { + res->instlist[i] = idp->it_set[i].i_inst; + if ((res->namelist[i] = strdup(idp->it_set[i].i_name)) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + } + } + else if (name == NULL) { + /* given an inst, return the name */ + for (i = 0; i < idp->it_numinst; i++) { + char *p; + if (inst == idp->it_set[i].i_inst) { + if ((res->namelist[0] = strdup(idp->it_set[i].i_name)) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + for (p = res->namelist[0]; *p; p++) { + if (*p == ' ') { + *p = '\0'; + break; + } + } + break; + } + } + if (i == idp->it_numinst) + err = 1; + } + else if (inst == PM_IN_NULL) { + /* given a name, return an inst */ + char *p; + long len; + for (p = name; *p; p++) { + if (*p == ' ') + break; + } + len = p - name; + for (i = 0; i < idp->it_numinst; i++) { + if (strncmp(name, idp->it_set[i].i_name, len) == 0 && + strlen(idp->it_set[i].i_name) >= len && + (idp->it_set[i].i_name[len] == '\0' || idp->it_set[i].i_name[len] == ' ')) { + res->instlist[0] = idp->it_set[i].i_inst; + break; + } + } + if (i == idp->it_numinst) + err = 1; + } + else + err = 1; + if (err == 1) { + /* bogus arguments or instance id/name */ + __pmFreeInResult(res); + return PM_ERR_INST; + } + + *result = res; + return 0; +} + +static int +sample_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + int i; + const char *p; + + /* skip the sample. or sampledso. part */ + for (p = name; *p != '.' && *p; p++) + ; + if (*p == '.') p++; + + for (i = 0; i < numdyn; i++) { + if (strcmp(p, dynamic_ones[i].name) == 0) { + *pmid = dynamic_ones[i].pmid; + return 0; + } + } + + return PM_ERR_NAME; +} + +static int +sample_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + size_t len = 0; + int nmatch = 0; + int i; + char *pfx; + char *p; + char **list; + + if (_isDSO) + pfx = "sampledso."; + else + pfx = "sample."; + + for (i = 0; i < numdyn; i++) { + if (dynamic_ones[i].pmid == pmid) { + nmatch++; + len += strlen(pfx)+strlen(dynamic_ones[i].name)+1; + } + } + + if (nmatch == 0) + return PM_ERR_PMID; + + len += nmatch*sizeof(char *); /* pointers to names */ + + if ((list = (char **)malloc(len)) == NULL) + return -oserror(); + + p = (char *)&list[nmatch]; + nmatch = 0; + for (i = 0; i < numdyn; i++) { + if (dynamic_ones[i].pmid == pmid) { + list[nmatch++] = p; + strcpy(p, pfx); + p += strlen(pfx); + strcpy(p, dynamic_ones[i].name); + p += strlen(dynamic_ones[i].name); + *p++ = '\0'; + } + } + *nameset = list; + + return nmatch; +} + +static int +sample_children(const char *name, int traverse, char ***offspring, int **status, pmdaExt *pmda) +{ + int i; + int j; + int nmatch; + int pfxlen; + int namelen; + const char *p; + char *q; + char *qend = NULL; + char **chn = NULL; + int *sts = NULL; + size_t len = 0; + size_t tlen = 0; + + /* skip the sample. or sampledso. part */ + for (p = name; *p != '.' && *p; p++) + ; + pfxlen = p - name; + if (*p == '.') p++; + namelen = strlen(p); + + nmatch = 0; + for (i = 0; i < numdyn; i++) { + q = dynamic_ones[i].name; + if (strncmp(p, q, namelen) != 0) { + /* no prefix match */ + dynamic_ones[i].mark = 0; + continue; + } + if (traverse == 0 && q[namelen] != '.') { + /* cannot be a child of name */ + dynamic_ones[i].mark = 0; + continue; + } + if (traverse == 1 && q[namelen] != '.' && q[namelen] != '\0') { + /* cannot be name itself, not a child of name */ + dynamic_ones[i].mark = 0; + continue; + } + if (traverse == 0) { + qend = &q[namelen+1]; + while (*qend && *qend != '.') + qend++; + tlen = qend - &q[namelen+1]; + for (j = 0; j < nmatch; j++) { + if (strncmp(&q[namelen+1], chn[j], tlen) == 0) { + /* already seen this child ... skip it */ + break; + } + } + } + else { + /* traversal ... need this one */ + j = nmatch; + } + if (j == nmatch) { + nmatch++; + if ((chn = (char **)realloc(chn, nmatch*sizeof(chn[0]))) == NULL) { + j = -oserror(); + goto fail; + } + if ((sts = (int *)realloc(sts, nmatch*sizeof(sts[0]))) == NULL) { + j = -oserror(); + goto fail; + } + if (traverse == 0) { + /* + * descendents only ... just want the next component of + * PMNS name + */ + if ((chn[nmatch-1] = (char *)malloc(tlen+1)) == NULL) { + j = -oserror(); + goto fail; + } + strncpy(chn[nmatch-1], &q[namelen+1], tlen); + chn[nmatch-1][tlen] = '\0'; + if (*qend == '.') + sts[nmatch-1] = PMNS_NONLEAF_STATUS; + else + sts[nmatch-1] = PMNS_LEAF_STATUS; + } + else { + /* + * traversal ... want the whole name including the prefix + * part + */ + tlen = pfxlen + strlen(dynamic_ones[i].name) + 2; + if ((chn[nmatch-1] = malloc(tlen)) == NULL) { + j = -oserror(); + goto fail; + } + strncpy(chn[nmatch-1], name, pfxlen); + chn[nmatch-1][pfxlen] = '.'; + chn[nmatch-1][pfxlen+1] = '\0'; + strcat(chn[nmatch-1], dynamic_ones[i].name); + sts[nmatch-1] = PMNS_LEAF_STATUS; + } + len += tlen + 1; + } + } + if (nmatch == 0) { + *offspring = NULL; + *status = NULL; + } + else { + if ((chn = (char **)realloc(chn, nmatch*sizeof(chn[0])+len)) == NULL) { + j = -oserror(); + goto fail; + } + q = (char *)&chn[nmatch]; + for (j = 0; j < nmatch; j++) { + strcpy(q, chn[j]); + free(chn[j]); + chn[j] = q; + q += strlen(chn[j])+1; + } + *offspring = chn; + *status = sts; + } + return nmatch; + +fail: + /* + * come here with j as negative error code, and some allocation failure for + * sts[] or chn[] or chn[nmatch-1][] + */ + if (sts != NULL) free(sts); + if (chn != NULL) { + for (i = 0; i < nmatch-1; i++) { + if (chn[i] != NULL) free(chn[i]); + } + free(chn); + } + return j; +} + +static int +sample_attribute(int ctx, int attr, const char *value, int length, pmdaExt *pmda) +{ + /* + * We have no special security or other requirements, so we're just + * going to log any connection attribute messages we happen to get + * from pmcd (handy for demo and testing purposes). + */ + if (pmDebug & DBG_TRACE_AUTH) { + char buffer[256]; + + if (!__pmAttrStr_r(attr, value, buffer, sizeof(buffer))) { + __pmNotifyErr(LOG_ERR, "Bad Attribute: ctx=%d, attr=%d\n", ctx, attr); + } else { + buffer[sizeof(buffer)-1] = '\0'; + __pmNotifyErr(LOG_INFO, "Attribute: ctx=%d %s", ctx, buffer); + } + } + return 0; +} + +/* + * high precision counter + */ +typedef union { + __uint32_t half[2]; + __uint64_t full; +} pmHPC_t; + +#ifdef HAVE_NETWORK_BYTEORDER +#define PM_HPC_TOP 0 +#define PM_HPC_BOTTOM 1 +#else +#define PM_HPC_TOP 1 +#define PM_HPC_BOTTOM 0 +#endif + +void +_pmHPCincr(pmHPC_t *ctr, __uint32_t val) +{ + if (val < ctr->half[PM_HPC_BOTTOM]) + /* assume single overflow */ + ctr->half[PM_HPC_TOP]++; + ctr->half[PM_HPC_BOTTOM] = val; +} + +static pmHPC_t rapid_ctr; + +static int +sample_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *ep) +{ + int i; /* over pmidlist[] */ + int j; /* over vset->vlist[] */ + int sts; + int need; + int inst; + int numval; + static pmResult *res; + static int maxnpmids; + static int nbyte; + __uint32_t *ulp; + unsigned long ul; + struct timeval now; + pmValueSet *vset; + pmDesc *dp; + __pmID_int *pmidp; + pmAtomValue atom; + int type; + + sample_inc_recv(ep->e_context); + sample_inc_xmit(ep->e_context); + + if (not_ready > 0) { + return limbo(); + } + + if (numpmid > maxnpmids) { + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = (int)sizeof(pmResult) + (numpmid - 1) * (int)sizeof(pmValueSet *); + if ((res = (pmResult *)malloc(need)) == NULL) + return -oserror(); + maxnpmids = numpmid; + } + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + if (need_mirage && (j = redo_mirage()) < 0) + return j; + if (need_dynamic && (j = redo_dynamic()) < 0) + return j; + + if (new_dodgey < 0) + redo_dodgey(); + + for (i = 0; i < numpmid; i++) { + pmidp = (__pmID_int *)&pmidlist[i]; + + if (direct_map) { + j = pmidp->item; + if (j < ndesc && desctab[j].pmid == pmidlist[i]) { + dp = &desctab[j]; + goto doit; + } + } + for (dp = desctab; dp->pmid != PM_ID_NULL; dp++) { + if (dp->pmid == pmidlist[i]) + break; + } +doit: + + if (dp->pmid != PM_ID_NULL) { + /* the special cases */ + if (pmidp->cluster == 0 && pmidp->item == 86) { + dp = &magic; + numval = 1; + } + else if (pmidp->cluster == 0 && pmidp->item == 54) + numval = PM_ERR_PMID; + else if (pmidp->cluster == 0 && pmidp->item == 92) /* darkness */ + numval = 0; + else if (pmidp->cluster == 0 && pmidp->item == 138) /* bad.novalues */ + numval = 0; + else if (pmidp->cluster == 0 && + (pmidp->item == 127 || /* event.type */ + pmidp->item == 128 || /* event.param_32 */ + pmidp->item == 129 || /* event.param_u32 */ + pmidp->item == 130 || /* event.param_64 */ + pmidp->item == 131 || /* event.param_u64 */ + pmidp->item == 132 || /* event.param_float */ + pmidp->item == 133 || /* event.param_double */ + pmidp->item == 134 || /* event.param_string */ + pmidp->item == 135)) /* event.param_aggregate */ + numval = 0; + else if (dp->type == PM_TYPE_NOSUPPORT) + numval = PM_ERR_APPVERSION; + else if (dp->indom != PM_INDOM_NULL) { + /* count instances in the profile */ + numval = 0; + /* special case(s) */ + if (pmidp->cluster == 0 && pmidp->item == 49) { + int kp; + /* needprofile - explict instances required */ + + numval = PM_ERR_PROFILE; + for (kp = 0; kp < _profile->profile_len; kp++) { + if (_profile->profile[kp].indom != dp->indom) + continue; + if (_profile->profile[kp].state == PM_PROFILE_EXCLUDE && + _profile->profile[kp].instances_len != 0) + numval = 0; + break; + } + } + else if (pmidp->cluster == 0 && (pmidp->item == 76 || pmidp->item == 77 || pmidp->item == 78)) { + /* + * if $(PCP_VAR_DIR)/pmdas/sample/dynamic.indom is not present, + * then numinst will be zero after the redo_dynamic() call + * in sample_init(), which makes zero loops through the + * fetch loop, so cannot set need_dynamic there ... + * do it here if not already turned on + */ + if (need_dynamic == 0) { + need_dynamic = 1; + if ((j = redo_dynamic()) < 0) + return j; + } + } + if (numval == 0) { + /* count instances in indom */ + startinst(dp->indom, 1); + while (nextinst(&inst)) { + /* special case ... not all here for part_bin */ + if (pmidp->cluster == 0 && pmidp->item == 50 && (inst % 200) == 0) + continue; + numval++; + } + } + } + else { + /* special case(s) for singular instance domains */ + if (pmidp->cluster == 0 && pmidp->item == 9) { + /* surprise! no value available */ + numval = 0; + } + else + numval = 1; + } + } + else + numval = 0; + + /* Must use individual malloc()s because of pmFreeResult() */ + if (numval >= 1) + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) + + (numval - 1)*sizeof(pmValue)); + else + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) - + sizeof(pmValue)); + if (vset == NULL) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = numval; + vset->valfmt = PM_VAL_INSITU; + if (vset->numval <= 0) + continue; + + if (dp->indom == PM_INDOM_NULL) + inst = PM_IN_NULL; + else { + startinst(dp->indom, 0); + nextinst(&inst); + } + type = dp->type; + j = 0; + do { + if (pmidp->cluster == 0 && pmidp->item == 50 && inst % 200 == 0) + goto skip; + if (pmidp->cluster == 0 && pmidp->item == 51 && inst % 200 == 0) + inst += 50; + if (j == numval) { + /* more instances than expected! */ + numval++; + res->vset[i] = vset = (pmValueSet *)realloc(vset, + sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue)); + if (vset == NULL) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + } + vset->vlist[j].inst = inst; + /* + * we mostly have cluster 0, metric already found in desctab[] + * so no checking needed + */ + if (pmidp->cluster == 0) { + switch (pmidp->item) { + case 0: + atom.l = _control; + break; + case 1: + if (_mypid == 0) _mypid = (int)getpid(); + atom.ul = _mypid; + break; + case 2: + atom.ul = time(NULL) - _start; + break; + case 3: + __pmtimevalNow(&now); + atom.d = 1000 * __pmtimevalSub(&now, &_then); + break; + case 4: + atom.l = 42; + break; + case 5: + switch (inst) { + case 0: /* "red" */ + _x = (_x + 1) % 100; + atom.l = _x + 100; + break; + case 1: /* "green" */ + _x = (_x + 1) % 100; + atom.l = _x + 200; + break; + case 2: /* "blue" */ + _x = (_x + 1) % 100; + atom.l = _x + 300; + break; + } + break; + case 6: + case 48: + case 50: + case 51: + case 103: /* long.bin & long.bin_ctr */ + case 104: + case 121: /* scramble.bin */ + /* the value is the instance identifier (sic) */ + atom.l = inst; + break; + /* and ditto for all the other type variants of "bin" */ + case 105: /* ulong.bin & ulong.bin_ctr */ + case 106: + atom.ul = inst; + break; + case 107: /* float.bin & float.bin_ctr */ + case 108: + atom.f = inst; + break; + case 109: /* longlong.bin & longlong.bin_ctr */ + case 110: + atom.ll = inst; + break; + case 111: /* ulonglong.bin & ulonglong.bin_ctr */ + case 112: + atom.ull = inst; + break; + case 113: /* double.bin & double.bin_ctr */ + case 114: + atom.d = inst; + break; + case 7: + /* drift */ + _drift = _drift + _sign * (int)(lrand48() % 50); + if (_drift < 0) _drift = 0; + atom.l = _drift; + if ((lrand48() % 100) < 20) { + if (_sign == 1) + _sign = -1; + else + _sign = 1; + } + break; + case 63: /* step_counter */ + case 8: /* step every 30 seconds */ + atom.l = (1 + (time(NULL) - _start) / 30) * _step; + break; + case 40: + /* total pdu count for all contexts */ + atom.ll = (__int64_t)sample_get_recv(CTX_ALL) + (__int64_t)sample_get_xmit(CTX_ALL); + break; + case 41: + /* recv pdu count for all contexts */ + atom.l = sample_get_recv(CTX_ALL); + break; + case 42: + /* xmit pdu count for all contexts */ + atom.l = sample_get_xmit(CTX_ALL); + break; + case 43: + case 44: + case 45: + case 122: + case 123: + case 124: + case 125: + /* percontext.pdu */ + /* percontext.recv-pdu */ + /* percontext.xmit-pdu */ + /* percontext.control.ctx */ + /* percontext.control.active */ + /* percontext.control.start */ + /* percontext.control.end */ + atom.l = sample_ctx_fetch(ep->e_context, pmidp->item); + break; + case 37: + /* mirage */ + _x = (_x + 1) % 100; + atom.l = (inst + 1) * 100 - _x; + need_mirage = 1; + break; + case 36: + /* write_me */ + atom.l = _write_me; + break; + case 39: + /* sysinfo */ + if (!sivb) { + /* malloc and init the pmValueBlock for + * sysinfo first type around */ + + int size = sizeof(pmValueBlock) - sizeof(int); + +#ifdef IS_SOLARIS + size += MAX_SYSNAME; +#else + size += sizeof (struct sysinfo); +#endif + + if ((sivb = calloc(1, size)) == NULL ) + return PM_ERR_GENERIC; + + sivb->vlen = size; + sivb->vtype = PM_TYPE_AGGREGATE; + } + +#ifdef HAVE_SYSINFO +#ifdef IS_SOLARIS + sysinfo(SI_SYSNAME, sivb->vbuf, MAX_SYSNAME); +#else + sysinfo((struct sysinfo *)sivb->vbuf); +#endif +#else + strncpy((char *)sivb->vbuf, si.dummy, sizeof(struct sysinfo)); +#endif + atom.vbp = sivb; + + /* + * pv:782029 The actual type must be PM_TYPE_AGGREGATE, + * but we have to tell pmStuffValue it's a + * PM_TYPE_AGGREGATE_STATIC + */ + type = PM_TYPE_AGGREGATE_STATIC; + break; + case 46: + if (_n46 == 0) { + _col46 = lrand48() % 3; + _n46 = 1 + (int)(lrand48() % 10); + } + _n46--; + switch (_col46) { + case 0: + atom.cp = "red"; + break; + case 1: + atom.cp = "yellow"; + break; + case 2: + atom.cp = "green"; + break; + } + break; + case 47: + if (_n47 == 0) { + _mag47 = 1 << (1 + (int)(lrand48() % 6)); + _n47 = 1 + (int)(lrand48() % 5); + } + _n47--; + atom.l = (__int32_t)_mag47; + break; + case 38: + /* mirage-longlong */ + _x = (_x + 1) % 100; + atom.ll = (inst + 1) * 100 - _x; + atom.ll *= 1000000; + need_mirage = 1; + break; + case 49: + /* need profile */ + switch (inst) { + case 0: /* "colleen" */ + atom.f = 3.05; + break; + case 1: /* "terry" */ + atom.f = 12.05; + break; + case 2: /* "emma" */ + case 3: /* "cathy" */ + atom.f = 11.09; + break; + case 4: /* "alexi" */ + atom.f = 5.26; + break; + } + break; + case 10: /* long.* group */ + atom.l = 1; + break; + case 11: + atom.l = 10; + break; + case 12: + atom.l = 100; + break; + case 13: + atom.l = 1000000; + break; + case 14: + atom.l = (__int32_t)_long; + break; + case 20: /* longlong.* group */ +#if !defined(HAVE_CONST_LONGLONG) + atom.ll = 1; +#else + atom.ll = 1LL; +#endif + break; + case 21: +#if !defined(HAVE_CONST_LONGLONG) + atom.ll = 10; +#else + atom.ll = 10LL; +#endif + break; + case 22: +#if !defined(HAVE_CONST_LONGLONG) + atom.ll = 100; +#else + atom.ll = 100LL; +#endif + break; + case 23: +#if !defined(HAVE_CONST_LONGLONG) + atom.ll = 1000000; +#else + atom.ll = 1000000LL; +#endif + break; + case 24: + atom.ll = _longlong; + break; + case 15: /* float.* group */ + atom.f = 1; + break; + case 16: + atom.f = 10; + break; + case 17: + atom.f = 100; + break; + case 18: + atom.f = 1000000; + break; + case 19: + atom.f = _float; + break; + case 25: /* double.* group */ + atom.d = 1; + break; + case 26: + atom.d = 10; + break; + case 27: + atom.d = 100; + break; + case 28: + atom.d = 1000000; + break; + case 29: + atom.d = _double; + break; + case 30: + atom.cp = ""; + break; + case 31: + atom.cp = "hullo world!"; + break; + case 32: + atom.cp = _string; + break; + case 33: + atom.vbp = _aggr33; + break; + case 34: + atom.vbp = _aggr34; + break; + case 35: + atom.vbp = _aggr35; + break; + case 52: + atom.l = inst; + break; + case 53: + atom.l = 499 - inst; + break; + case 56: + atom.l = not_ready; + break; + case 57: + _wrap += INT_MAX / 2 - 1; + atom.l = _wrap; + break; + case 58: + _u_wrap += UINT_MAX / 2 - 1; + atom.ul = _u_wrap; + break; + case 59: + _ll_wrap += LONGLONG_MAX / 2 - 1; + atom.ll = _ll_wrap; + break; + case 60: + _ull_wrap += ULONGLONG_MAX / 2 - 1; + atom.ull = _ull_wrap; + break; + case 61: + atom.l = dodgey; + break; + case 62: + if (dodgey > 5 && j == 0) + new_dodgey--; + if (tmp_dodgey <= 0) { + j = tmp_dodgey; + goto done; + } + else if (tmp_dodgey <= 5) { + if (inst > tmp_dodgey) + goto skip; + } + atom.l = (int)(lrand48() % 101); + break; + case 64: + _rapid += 80000000; + _pmHPCincr(&rapid_ctr, _rapid); + atom.ul = (__uint32_t)(rapid_ctr.full * 10); + break; + case 65: /* scale_step.bytes_up */ + atom.d = scale_step_bytes_up; + if (++scale_step_number[0] % 5 == 0) { + if (scale_step_bytes_up < 1024.0*1024.0*1024.0*1024.0) + scale_step_bytes_up *= 2; + else + scale_step_bytes_up = 1; + } + break; + case 66: /* scale_step.bytes_down */ + atom.d = scale_step_bytes_down; + if (++scale_step_number[1] % 5 == 0) { + if (scale_step_bytes_down > 1) + scale_step_bytes_down /= 2; + else + scale_step_bytes_down = 1024.0*1024.0*1024.0*1024.0; + } + break; + case 67: /* scale_step.count_up */ + atom.d = scale_step_count_up; + if (++scale_step_number[2] % 5 == 0) { + if (scale_step_count_up < 1.0e12) + scale_step_count_up *= 10; + else + scale_step_count_up = 1; + } + break; + case 68: /* scale_step.count_down */ + atom.d = scale_step_count_down; + if (++scale_step_number[3] % 5 == 0) { + if (scale_step_count_down > 1) + scale_step_count_down /= 10; + else + scale_step_count_down = 1.0e12; + } + break; + case 69: /* scale_step.time_up_secs */ + atom.d = scale_step_time_up_secs; + if (++scale_step_number[4] % 5 == 0) { + if (scale_step_time_up_secs < 60*60*24) + scale_step_time_up_secs *= 10; + else + scale_step_time_up_secs = 1; + } + break; + case 70: /* scale_step.time_up_nanosecs */ + atom.d = scale_step_time_up_nanosecs; + if (++scale_step_number[5] % 5 == 0) { + if (scale_step_time_up_nanosecs < 1e9*60*60*24) + scale_step_time_up_nanosecs *= 10; + else + scale_step_time_up_nanosecs = 1; + } + break; + case 71: /* scale_step.none_up */ + atom.d = scale_step_none_up; + if (++scale_step_number[6] % 5 == 0) { + if (scale_step_none_up < 10000000) + scale_step_none_up *= 10; + else + scale_step_none_up = 1; + } + break; + case 72: /* const_rate.value */ + __pmtimevalNow(&now); + atom.ul = const_rate_value + const_rate_gradient * __pmtimevalSub(&now, &const_rate_timestamp); + const_rate_timestamp = now; + const_rate_value = atom.ul; + break; + case 73: /* const_rate.gradient */ + atom.ul = const_rate_gradient; + break; + case 74: /* error_code */ + atom.l = _error_code; + break; + case 75: /* error_check */ + if (_error_code < 0) + return _error_code; + atom.l = 0; + break; + case 76: /* dynamic.counter */ + case 77: /* dynamic.discrete */ + case 78: /* dynamic.instant */ + if (inst > _dyn_max) { + /* bad instance! */ + goto done; + } + atom.l = _dyn_ctr[inst]; + break; + case 79: /* many.count */ + atom.l=many_count; + break; + case 80: /* many.int */ + atom.l = inst; + break; + case 81: /* byte_ctr */ + nbyte += lrand48() % 1024; + atom.l = nbyte; + break; + case 82: /* byte_rate */ + atom.l = (int)(lrand48() % 1024); + break; + case 83: /* kbyte_ctr */ + nbyte += lrand48() % 1024; + atom.l = nbyte; + break; + case 84: /* kbyte_rate */ + atom.l = (int)(lrand48() % 1024); + break; + case 85: /* byte_rate_per_hour */ + atom.l = (int)(lrand48() % 1024); + break; + case 86: /* dynamic.meta.metric */ + switch (magic.type) { + case PM_TYPE_32: + atom.l = 42; + break; + case PM_TYPE_U32: + atom.ul = 42; + break; + case PM_TYPE_64: + atom.ll = 42; + break; + case PM_TYPE_U64: + atom.ull = 42; + break; + case PM_TYPE_FLOAT: + atom.f = 42; + break; + case PM_TYPE_DOUBLE: + atom.d = 42; + break; + default: + /* do nothing in other cases ... return garbage */ + break; + } + break; + case 87: /* dynamic.meta.pmdesc.type */ + atom.ul = magic.type; + break; + case 88: /* dynamic.meta.pmdesc.indom */ + atom.ul = magic.indom; + break; + case 89: /* dynamic.meta.pmdesc.sem */ + atom.ul = magic.sem; + break; + case 90: /* dynamic.meta.pmdesc.units */ + ulp = (__uint32_t *)&magic.units; + atom.ul = *ulp; + break; + case 91: /* datasize */ + __pmProcessDataSize(&ul); + atom.ul = ul; + break; + /* no case 92 for darkeness, handled above */ + case 93: /* ulong.* group */ + atom.ul = 1; + break; + case 94: + atom.ul = 10; + break; + case 95: + atom.ul = 100; + break; + case 96: + atom.ul = 1000000; + break; + case 97: + atom.ul = (__int32_t)_ulong; + break; + case 98: /* ulonglong.* group */ +#if !defined(HAVE_CONST_LONGLONG) + atom.ull = 1; +#else + atom.ull = 1ULL; +#endif + break; + case 99: +#if !defined(HAVE_CONST_LONGLONG) + atom.ull = 10; +#else + atom.ull = 10ULL; +#endif + break; + case 100: +#if !defined(HAVE_CONST_LONGLONG) + atom.ull = 100; +#else + atom.ull = 100ULL; +#endif + break; + case 101: +#if !defined(HAVE_CONST_LONGLONG) + atom.ull = 1000000; +#else + atom.ull = 1000000ULL; +#endif + break; + case 102: + atom.ull = _ulonglong; + break; + case 115: /* ulong.count.base */ + atom.ul = 42000000; + break; + case 116: /* ulong.count.deca */ + atom.ul = 4200000; + break; + case 117: /* ulong.count.hecto */ + atom.ul = 420000; + break; + case 118: /* ulong.count.kilo */ + atom.ul = 42000; + break; + case 119: /* ulong.count.mega */ + atom.ul = 42; + break; + case 120: /* scramble.version */ + atom.ll = scramble_ver; + break; + case 126: /* event.reset */ + atom.l = event_get_fetch_count(); + break; + case 136: /* event.records */ + case 137: /* event.no_indom_records */ + if ((sts = sample_fetch_events(&atom.vbp, inst)) < 0) + return sts; + break; + case 139: /* event.highres_records */ + if ((sts = sample_fetch_highres_events(&atom.vbp, inst)) < 0) + return sts; + break; + case 140: /* event.reset_highres */ + atom.l = event_get_highres_fetch_count(); + break; + case 1000: /* secret.bar */ + atom.cp = "foo"; + break; + case 1001: /* secret.foo.one */ + atom.l = 1; + break; + case 1002: /* secret.foo.two */ + atom.l = 2; + break; + case 1003: /* secret.foo.bar.three */ + atom.l = 3; + break; + case 1004: /* secret.foo.bar.four */ + atom.l = 4; + break; + case 1005: /* secret.foo.bar.grunt.five */ + atom.l = 5; + break; + case 1006: /* secret.foo.bar.grunt.snort.six */ + atom.l = 6; + break; + case 1007: /* secret.foo.bar.grunt.snort.seven */ + atom.l = 7; + break; + case 1023: /* bigid */ + atom.l = 4194303; + break; + } + } + if ((sts = __pmStuffValue(&atom, &vset->vlist[j], type)) < 0) { + __pmFreeResultValues(res); + return sts; + } + vset->valfmt = sts; + j++; /* next element in vlist[] for next instance */ + +skip: + ; + } while (dp->indom != PM_INDOM_NULL && nextinst(&inst)); +done: + vset->numval = j; + } + *resp = res; + return PMDA_FETCH_STATIC; +} + +static int +sample_desc(pmID pmid, pmDesc *desc, pmdaExt *ep) +{ + int i; + __pmID_int *pmidp = (__pmID_int *)&pmid; + + sample_inc_recv(ep->e_context); + sample_inc_xmit(ep->e_context); + + if (not_ready > 0) { + return limbo(); + } + + if (direct_map) { + i = pmidp->item; + if (i < ndesc && desctab[i].pmid == pmid) + goto doit; + } + for (i = 0; desctab[i].pmid != PM_ID_NULL; i++) { + if (desctab[i].pmid == pmid) { +doit: + /* the special cases */ + if (pmidp->item == 54) + return PM_ERR_PMID; + else if (pmidp->item == 75 && _error_code < 0) + /* error_check and error_code armed */ + return _error_code; + else if (pmidp->item == 86) + *desc = magic; + else + *desc = desctab[i]; + return 0; + } + } + return PM_ERR_PMID; +} + +static int +sample_text(int ident, int type, char **buffer, pmdaExt *ep) +{ + int sts; + + sample_inc_recv(ep->e_context); + sample_inc_xmit(ep->e_context); + + if (not_ready > 0) { + return limbo(); + } + + if (ident & PM_TEXT_PMID) { + __pmID_int *pmidp = (__pmID_int *)&ident; + int i; + + if (direct_map) { + i = pmidp->item; + if (i < ndesc && desctab[i].pmid == (pmID)ident) + goto doit; + } + for (i = 0; desctab[i].pmid != PM_ID_NULL; i++) { + if (desctab[i].pmid == (pmID)ident) { +doit: + /* the special cases */ + if (pmidp->item == 75 && _error_code < 0) + /* error_check and error_code armed */ + return _error_code; + break; + } + } + } + + sts = pmdaText(ident, type, buffer, ep); + + return sts; +} + +static int +sample_store(pmResult *result, pmdaExt *ep) +{ + int i; + pmValueSet *vsp; + pmDesc *dp; + __pmID_int *pmidp; + int sts = 0; + __int32_t *lp; + pmAtomValue av; + + sample_inc_recv(ep->e_context); + sample_inc_xmit(ep->e_context); + + if (not_ready > 0) { + return limbo(); + } + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + for (dp = desctab; dp->pmid != PM_ID_NULL; dp++) { + if (dp->pmid == vsp->pmid) + break; + } + if (dp->pmid == PM_ID_NULL) { + /* not one of our metrics */ + sts = PM_ERR_PMID; + break; + } + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster != 0) { + sts = PM_ERR_PMID; + break; + } + + /* + * for this PMD, the metrics that support modification + * via pmStore() mostly demand a single value, encoded in + * the result structure as PM_VAL_INSITU format + */ + switch (pmidp->item) { + case 24: /* longlong.write_me */ + case 29: /* double.write_me */ + case 32: /* string.write_me */ + case 35: /* aggregate.write_me */ + case 102: /* ulonglong.write_me */ + case 120: /* scramble.ver */ + if (vsp->numval != 1 || vsp->valfmt == PM_VAL_INSITU) + sts = PM_ERR_CONV; + break; + + case 74: /* error_code */ + case 73: /* const_rate.gradient */ + case 61: /* dodgey.control */ + case 56: /* not_ready */ + case 36: + case 42: + case 41: + case 14: /* long.write_me */ + case 8: /* step */ + case 7: /* drift */ + case 0: /* control */ + case 79: /* many.count */ + case 87: /* dynamic.meta.pmdesc.type */ + case 88: /* dynamic.meta.pmdesc.indom */ + case 89: /* dynamic.meta.pmdesc.sem */ + case 90: /* dynamic.meta.pmdesc.units */ + case 97: /* ulong.write_me */ + case 126: /* event.reset */ + case 140: /* event.reset_highres */ + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_INSITU) + sts = PM_ERR_CONV; + break; + + case 19: /* float.write_me */ + if (vsp->numval != 1) + sts = PM_ERR_CONV; + /* accommodate both old and new encoding styles for floats */ + break; + + case 40: /* pdu */ + /* value is ignored, so valfmt does not matter */ + if (vsp->numval != 1) + sts = PM_ERR_CONV; + break; + + default: + sts = PM_ERR_PERMISSION; + break; + + } + if (sts != 0) + break; + + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], dp->type, &av, dp->type)) < 0) + break; + + /* + * we only have cluster 0, metric already found in desctab[], + * so no checking needed nor outer case on pmidp->cluster + */ + switch (pmidp->item) { + case 0: /* control */ + _control = av.l; + switch (_control) { + case -1: + /* terminate, if we are not a DSO implementation */ + sample_done = 1; + break; + default: + pmDebug = _control; + break; + } + break; + case 7: /* drift */ + _drift = av.l; + break; + case 8: /* step */ + _step = av.l; + break; + case 14: /* long.write_me */ + _long = av.l; + break; + case 24: /* longlong.write_me */ + _longlong = av.ll; + break; + case 19: /* float.write_me */ + _float = av.f; + break; + case 40: /* pdu */ + /* + * for the pdu group, the value is ignored, and the only + * operation is to reset the counter(s) + */ + sample_clr_recv(CTX_ALL); + sample_clr_xmit(CTX_ALL); + break; + case 41: + sample_clr_recv(CTX_ALL); + break; + case 42: + sample_clr_xmit(CTX_ALL); + break; + case 36: + _write_me = av.l; + break; + case 29: /* double.write_me */ + _double = av.d; + break; + case 32: /* string.write_me */ + free(_string); + _string = av.cp; + break; + case 35: /* aggregate.write_me */ + free(_aggr35); + _aggr35 = av.vbp; + break; + case 56: /* not_ready */ + not_ready = av.l; + break; + case 61: /* dodgey.control */ + dodgey = av.l; + redo_dodgey(); + break; + case 73: /* const_rate.gradient */ + const_rate_gradient = av.ul; + break; + case 74: /* error_code */ + _error_code = av.l; + break; + case 79: /* many.count */ + many_count = av.l; + /* change the size of the many instance domain */ + _error_code = redo_many(); + break; + case 87: /* dynamic.meta.pmdesc.type */ + magic.type = av.l; + break; + case 88: /* dynamic.meta.pmdesc.indom */ + magic.indom = av.l; + break; + case 89: /* dynamic.meta.pmdesc.sem */ + magic.sem = av.l; + break; + case 90: /* dynamic.meta.pmdesc.units */ + lp = (__int32_t *)&magic.units; + *lp = av.l; + break; + case 97: /* ulong.write_me */ + _ulong = av.ul; + break; + case 102: /* ulonglong.write_me */ + _ulonglong = av.ull; + break; + case 120: /* scramble.version */ + scramble_ver = 0; + for (i = 0; i < indomtab[BIN_INDOM].it_numinst; i++) { + indomtab[SCRAMBLE_INDOM].it_set[i].i_inst = indomtab[BIN_INDOM].it_set[i].i_inst; + indomtab[SCRAMBLE_INDOM].it_set[i].i_name = indomtab[BIN_INDOM].it_set[i].i_name; + } + indomtab[SCRAMBLE_INDOM].it_numinst = indomtab[BIN_INDOM].it_numinst; + break; + case 126: /* event.reset */ + event_set_fetch_count(av.l); + break; + case 140: /* event.reset_highres */ + event_set_highres_fetch_count(av.l); + break; + default: + sts = PM_ERR_PERMISSION; + break; + } + } + + return sts; +} + +void +__PMDA_INIT_CALL +sample_init(pmdaInterface *dp) +{ + char helppath[MAXPATHLEN]; + int i; + + if (_isDSO) { + int sep = __pmPathSeparator(); + snprintf(helppath, sizeof(helppath), "%s%c" "sample" "%c" "dsohelp", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_LATEST, "sample DSO", helppath); + } + else { + __pmProcessDataSize(NULL); + } + + if (dp->status != 0) + return; + dp->comm.flags |= PDU_FLAG_AUTH; + + dp->version.any.fetch = sample_fetch; + dp->version.any.desc = sample_desc; + dp->version.any.instance = sample_instance; + dp->version.any.text = sample_text; + dp->version.any.store = sample_store; + dp->version.any.profile = sample_profile; + dp->version.four.pmid = sample_pmid; + dp->version.four.name = sample_name; + dp->version.four.children = sample_children; + dp->version.six.attribute = sample_attribute; + pmdaSetEndContextCallBack(dp, sample_ctx_end); + + pmdaInit(dp, NULL, 0, NULL, 0); /* don't use indomtab or metrictab */ + + __pmtimevalNow(&_then); + _start = time(NULL); + init_tables(dp->domain); + init_events(dp->domain); + redo_mirage(); + redo_dynamic(); + + /* initialization of domain in PMIDs for dynamic PMNS entries */ + for (i = 0; i < numdyn; i++) { + ((__pmID_int *)&dynamic_ones[i].pmid)->domain = dp->domain; + } + /* + * Max Matveev wanted this sort of redirection, so first entry is + * actually a redirect to PMID 2.4.1 (pmcd.agent.status) + */ + ((__pmID_int *)&dynamic_ones[0].pmid)->domain = 2; + ((__pmID_int *)&dynamic_ones[0].pmid)->cluster = 4; + ((__pmID_int *)&dynamic_ones[0].pmid)->item = 1; + + /* + * for gcc/egcs, statically initializing these cased the strings + * to be read-only, causing SEGV in redo_dynamic ... so do the + * initialization dynamically here. + */ + _dodgey[0].i_name = strdup("d1"); + _dodgey[1].i_name = strdup("d2"); + _dodgey[2].i_name = strdup("d3"); + _dodgey[3].i_name = strdup("d4"); + _dodgey[4].i_name = strdup("d5"); +} diff --git a/src/pmdas/sendmail/GNUmakefile b/src/pmdas/sendmail/GNUmakefile new file mode 100644 index 0000000..171c7c0 --- /dev/null +++ b/src/pmdas/sendmail/GNUmakefile @@ -0,0 +1,58 @@ +# +# Copyright (c) 2000-2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = sendmail +DOMAIN = SENDMAIL +CMDTARGET = $(IAM)$(EXECSUFFIX) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +CFILES = sendmail.c +SCRIPTS = Install Remove +DFILES = README +LSRCFILES= $(SCRIPTS) pmns help root Sendmail.pmchart $(DFILES) + +VERSION_SCRIPT = exports +PMDAINIT = $(IAM)_init +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMCHART = $(PCP_VAR_DIR)/config/pmchart + +LDIRT = domain.h *.o $(IAM).log $(LIBTARGET) $(CMDTARGET) $(VERSION_SCRIPT) +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) + +default_pcp default: $(CMDTARGET) $(LIBTARGET) + +include $(BUILDRULES) + +install install_pcp: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(PMDADIR)/$(LIBTARGET) + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/pmda$(IAM)$(EXECSUFFIX) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) pmns help root domain.h $(PMDADIR) + $(INSTALL) -m 644 Sendmail.pmchart $(PMCHART)/Sendmail + +$(CMDTARGET): $(OBJECTS) + +$(LIBTARGET): $(OBJECTS) $(VERSION_SCRIPT) + +sendmail.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) diff --git a/src/pmdas/sendmail/Install b/src/pmdas/sendmail/Install new file mode 100644 index 0000000..5ce3c57 --- /dev/null +++ b/src/pmdas/sendmail/Install @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the sendmail PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=sendmail +pmda_interface=3 +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/sendmail/README b/src/pmdas/sendmail/README new file mode 100644 index 0000000..1a89adc --- /dev/null +++ b/src/pmdas/sendmail/README @@ -0,0 +1,50 @@ +Sendmail PMDA +============= + +Export information from the sendmail statistics file. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT sendmail + +Installation +============ + + + # cd $PCP_PMDAS_DIR/sendmail + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + Everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/sendmail + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/sendmail.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/sendmail/Remove b/src/pmdas/sendmail/Remove new file mode 100644 index 0000000..c0275d7 --- /dev/null +++ b/src/pmdas/sendmail/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the sendmail PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=sendmail + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/sendmail/Sendmail.pmchart b/src/pmdas/sendmail/Sendmail.pmchart new file mode 100644 index 0000000..446decf --- /dev/null +++ b/src/pmdas/sendmail/Sendmail.pmchart @@ -0,0 +1,10 @@ +#pmchart +Version 2.0 host dynamic + +Chart Style plot + Plot Color #137bfe Host * Metric sendmail.total.bytes_from + Plot Color #fefa1a Host * Metric sendmail.total.bytes_to +Chart Style plot + Plot Color #1e1cfe Host * Metric sendmail.total.msgs_from + Plot Color #fe9913 Host * Metric sendmail.total.msgs_to + diff --git a/src/pmdas/sendmail/help b/src/pmdas/sendmail/help new file mode 100644 index 0000000..593469d --- /dev/null +++ b/src/pmdas/sendmail/help @@ -0,0 +1,73 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# sendmail PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ SENDMAIL.0 Instance domain "mailer" for sendmail PMDA +The mailers 0 (prog), 1 (*file*), and 2 (*include*) are fixed. Other +mailers are defined by the order of any additional "M" records in the +sendmail.cf file. + +@ sendmail.start_date Date on which sendmail stats file was created +The date in ctime(2) format on which the sendmail statistics file was +first created. All statistics are cumulative from that time. + +The sendmail statistics file is /var/sendmailst by default, but may be +redefined by an "OS" or "O StatusFile" record in the sendmail.cf file. + +@ sendmail.permailer.msgs_from Messages received from each mailer +Count of messages received from each "mailer" defined in sendmail's +configuration file (/etc/sendmail.cf). + +@ sendmail.permailer.bytes_from Kbytes received from each mailer +Count of Kbytes summed over all messages received from each "mailer" +defined in sendmail's configuration file (/etc/sendmail.cf). + +@ sendmail.permailer.msgs_to Messages sent to each mailer +Count of messages sent to each "mailer" defined in sendmail's +configuration file (/etc/sendmail.cf). + +@ sendmail.permailer.bytes_to Kbytes sent to each mailer +Count of Kbytes summed over all messages sent to each "mailer" defined +in sendmail's configuration file (/etc/sendmail.cf). + +@ sendmail.total.msgs_from Messages received from all mailers +Count of messages received by sendmail. + +@ sendmail.total.bytes_from Kbytes received from all mailers +Count of Kbytes summed over all messages received by sendmail. + +@ sendmail.total.msgs_to Messages sent to all mailers +Count of messages sent by sendmail. + +@ sendmail.total.bytes_to Kbytes sent to all mailers +Count of Kbytes summed over all messages sent by sendmail. + diff --git a/src/pmdas/sendmail/pmns b/src/pmdas/sendmail/pmns new file mode 100644 index 0000000..856448f --- /dev/null +++ b/src/pmdas/sendmail/pmns @@ -0,0 +1,39 @@ +/* + * Metrics for sendmail PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +sendmail { + start_date SENDMAIL:0:0 + permailer + total +} + +sendmail.permailer { + msgs_from SENDMAIL:1:0 + bytes_from SENDMAIL:1:1 + msgs_to SENDMAIL:1:2 + bytes_to SENDMAIL:1:3 +} + +sendmail.total { + msgs_from SENDMAIL:2:0 + bytes_from SENDMAIL:2:1 + msgs_to SENDMAIL:2:2 + bytes_to SENDMAIL:2:3 +} diff --git a/src/pmdas/sendmail/root b/src/pmdas/sendmail/root new file mode 100644 index 0000000..ead8f5a --- /dev/null +++ b/src/pmdas/sendmail/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { sendmail } + +#include "pmns" + diff --git a/src/pmdas/sendmail/sendmail.c b/src/pmdas/sendmail/sendmail.c new file mode 100644 index 0000000..7b2b542 --- /dev/null +++ b/src/pmdas/sendmail/sendmail.c @@ -0,0 +1,524 @@ +/* + * Sendmail PMDA + * + * Copyright (c) 1995-2000,2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include + +/* + * Sendmail PMDA + * + * This PMDA uses the statistics file that sendmail optionally maintains + * -- see "OS" or "O StatusFile=" in sendmail.cf and sendmail(1) + * + * This file (defaults to /var/sendmail.st) must be created before sendmail + * will update any statistics. + */ + +/* + * list of instances + */ + +static pmdaIndom indomtab[] = { +#define MAILER_INDOM 0 + { MAILER_INDOM, 0, NULL }, +}; + +static char *statsfile = "/var/sendmail.st"; +static char *username; +static int nmailer; +static void *ptr; +static struct stat laststatbuf; +static time_t *start_date; +static __uint32_t *msgs_from; +static __uint32_t *kbytes_from; +static __uint32_t *msgs_to; +static __uint32_t *kbytes_to; + +static pmdaMetric metrictab[] = { +/* start_date */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* permailer.msgs_from */ + { NULL, + { PMDA_PMID(1,0), PM_TYPE_U32, MAILER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* permailer.bytes_from */ + { NULL, + { PMDA_PMID(1,1), PM_TYPE_U32, MAILER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* permailer.msgs_to */ + { NULL, + { PMDA_PMID(1,2), PM_TYPE_U32, MAILER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* permailer.bytes_to */ + { NULL, + { PMDA_PMID(1,3), PM_TYPE_U32, MAILER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* total.msgs_from */ + { NULL, + { PMDA_PMID(2,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* total.bytes_from */ + { NULL, + { PMDA_PMID(2,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +/* total.msgs_to */ + { NULL, + { PMDA_PMID(2,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* total.bytes_to */ + { NULL, + { PMDA_PMID(2,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, }, +}; + +static void +map_stats(void) +{ + struct stat statbuf; + static int fd; + + static int notified = 0; +#define MAPSTATS_NULL 0x01 +#define MAPSTATS_NOTV2STRUCT 0x02 +#define MAPSTATS_MAPFAIL 0x04 + + /* From mailstats.h in sendmail(1) 8.x... */ + struct smstat_s + { +#define MAXMAILERS 25 +#define STAT_VERSION 2 +#define STAT_MAGIC 0x1B1DE + int stat_magic; /* magic number */ + int stat_version; /* stat file version */ + time_t stat_itime; /* file initialization time */ + short stat_size; /* size of this structure */ + long stat_nf[MAXMAILERS]; /* # msgs from each mailer */ + long stat_bf[MAXMAILERS]; /* kbytes from each mailer */ + long stat_nt[MAXMAILERS]; /* # msgs to each mailer */ + long stat_bt[MAXMAILERS]; /* kbytes to each mailer */ + long stat_nr[MAXMAILERS]; /* # rejects by each mailer */ + long stat_nd[MAXMAILERS]; /* # discards by each mailer */ + } *smstat; + + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: map_stats: Entering:\n", pmProgname); + fprintf(stderr, "%s: map_stats: Check: ptr = " PRINTF_P_PFX "%p\n", pmProgname, ptr); + fprintf(stderr, "%s: map_stats: Check: statsfile = " PRINTF_P_PFX "%p\n", pmProgname, statsfile); + if (statsfile != NULL) + fprintf(stderr, "%s: map_stats: = %s\n", pmProgname, statsfile); + } +#endif + + if (statsfile == NULL || stat(statsfile, &statbuf) < 0) { + /* if sendmail not collecting stats this is expected */ + if (ptr != NULL) { + /* must have gone away */ + __pmMemoryUnmap(ptr, laststatbuf.st_size); + close(fd); + ptr = NULL; + notified &= ~MAPSTATS_NOTV2STRUCT; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: map_stats: (Maybe) stat() < 0; pmunmap() called\n", pmProgname); + } +#endif + } + return; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: map_stats: Check: statbuf.st_ino = %lu\n", pmProgname, (unsigned long)statbuf.st_ino); + fprintf(stderr, "%s: map_stats: Check: statbuf.st_dev = %lu\n", pmProgname, (unsigned long)statbuf.st_dev); + fprintf(stderr, "%s: map_stats: Check: laststatbuf.st_ino = %lu\n", pmProgname, (unsigned long)laststatbuf.st_ino); + fprintf(stderr, "%s: map_stats: Check: laststatbuf.st_dev = %lu\n", pmProgname, (unsigned long)laststatbuf.st_dev); + } +#endif + if (statbuf.st_ino != laststatbuf.st_ino || + statbuf.st_dev != laststatbuf.st_dev || + ptr == NULL) { + /* + * Not the same as the file we saw last time, or statsfile is + * not mapped into memory (because it was zero length). + * + * The file can change due to rotation or restarting sendmail... + * note the times (st_atim, st_mtim and st_ctim) are all expected + * to change as sendmail updates the file, hence we must use dev + * and ino. + * + * ino is guaranteed to change for different instances of the + * sendmail stats file, since a mmap()'d file is never closed + * until after it's munmap()'d. + */ + + if (ptr != NULL) { + __pmMemoryUnmap(ptr, laststatbuf.st_size); + close(fd); + ptr = NULL; + notified &= ~MAPSTATS_NOTV2STRUCT; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: map_stats: statbuf.st_[dev|ido] changed; pmunmap() called\n", pmProgname); + } +#endif + } + + if ((fd = open(statsfile, O_RDONLY)) < 0) { + __pmNotifyErr(LOG_WARNING, "%s: map_stats: cannot open(\"%s\",...): %s", + pmProgname, statsfile, osstrerror()); + return; + } + ptr = __pmMemoryMap(fd, statbuf.st_size, 0); + if (ptr == NULL) { + if (!(notified & MAPSTATS_MAPFAIL)) { + __pmNotifyErr(LOG_ERR, "%s: map_stats: memmap of %s failed: %s", + pmProgname, statsfile, osstrerror()); + } + close(fd); + ptr = NULL; + notified |= MAPSTATS_MAPFAIL; + return; + } + + laststatbuf = statbuf; /* struct assignment */ + notified &= ~(MAPSTATS_NULL | MAPSTATS_MAPFAIL); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: map_stats: mmap() called, succeeded\n", pmProgname); + } +#endif + + /* Check for a statistics file from sendmail(1) 8.x: */ + smstat = (struct smstat_s *)ptr; + if (smstat->stat_magic != STAT_MAGIC || + smstat->stat_version != STAT_VERSION) { + if (! (notified & MAPSTATS_NOTV2STRUCT)) { + __pmNotifyErr(LOG_WARNING, "%s: map_stats: cannot find magic number in file %s; assuming version 1 format", + pmProgname, statsfile); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: map_stats: smstat_s contents:\n", pmProgname); + fprintf(stderr, "%s: map_stats: Version 2 format:\n", pmProgname); + fprintf(stderr, "%s: map_stats: Check: stat_magic = 0x%x\n", pmProgname, smstat->stat_magic); + fprintf(stderr, "%s: map_stats: Check: stat_version = 0x%x\n", pmProgname, smstat->stat_version); + fprintf(stderr, "%s: map_stats: Check: stat_itime = %s", pmProgname, ctime(&(smstat->stat_itime))); + fprintf(stderr, "%s: map_stats: Check: stat_size = %d\n", pmProgname, smstat->stat_size); + + /* We're being difficult here... using smstat_s the wrong way! */ + fprintf(stderr, "%s: map_stats: Version 1 format:\n", pmProgname); + fprintf(stderr, "%s: map_stats: Check: stat_itime = %s", pmProgname, ctime((time_t *)&(smstat->stat_magic))); + fprintf(stderr, "%s: map_stats: Check: stat_size = %d\n", pmProgname, *((short *)&(smstat->stat_version))); + } +#endif + notified |= MAPSTATS_NOTV2STRUCT; + } + + /* Could be older version of stats file... here is the original code + that dealt with that case */ + /* + * format of [older version] sendmail stats file: + * word[0] time_t file first created + * word[1] N/A + * word[2] .. word[K+2] msgs_from mailers 0 .. K + * word[K+3] .. word[2*K+3] kbytes_from mailers 0 .. K + * word[2*K+3] .. word[3*K+4] msgs_to mailers 0 .. K + * word[3*K+4] .. word[4*K+5] kbytes_to mailers 0 .. K + */ + nmailer = (statbuf.st_size - sizeof(__int32_t) - sizeof(__int32_t)) / (4 * sizeof(__uint32_t)); + msgs_from = &((__uint32_t *)ptr)[2]; + kbytes_from = &msgs_from[nmailer]; + msgs_to = &kbytes_from[nmailer]; + kbytes_to = &msgs_to[nmailer]; + start_date = (time_t *)ptr; + } + else { + /* Assign pointers to point to parts of the v2 struct */ + nmailer = ((char *)smstat->stat_bf - (char *)smstat->stat_nf) / sizeof(long); + msgs_from = (__uint32_t *)&(smstat->stat_nf); + kbytes_from = (__uint32_t *)&(smstat->stat_bf); + msgs_to = (__uint32_t *)&(smstat->stat_nt); + kbytes_to = (__uint32_t *)&(smstat->stat_bt); + start_date = &(smstat->stat_itime); + } + } +} + +/* + * logic here is similar to that used by mailstats(1) + */ +static void +do_sendmail_cf(void) +{ + FILE *fp; + char buf[MAXPATHLEN+20]; + char *bp; + int i; + int lineno = 0; + + if ((fp = fopen("/etc/sendmail.cf", "r")) == NULL) { + if ((fp = fopen("/etc/mail/sendmail.cf", "r")) == NULL) { + /* this is pretty serious! */ + nmailer = 0; + statsfile = NULL; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "Warning: cannot find sendmail.cf, so no stats!\n"); +#endif + return; + } + } + + nmailer = 3; + indomtab[MAILER_INDOM].it_set = (pmdaInstid *)malloc(nmailer * sizeof(pmdaInstid)); + indomtab[MAILER_INDOM].it_set[0].i_inst = 0; + indomtab[MAILER_INDOM].it_set[0].i_name = "prog"; + indomtab[MAILER_INDOM].it_set[1].i_inst = 1; + indomtab[MAILER_INDOM].it_set[1].i_name = "*file*"; + indomtab[MAILER_INDOM].it_set[2].i_inst = 2; + indomtab[MAILER_INDOM].it_set[2].i_name = "*include*"; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + lineno++; + bp = buf; + + if (*bp == 'M') { + /* mailer definition */ + bp++; + while (*bp != ',' && !isspace((int)*bp) && *bp != '\0') + bp++; + *bp = '\0'; + for (i = 0; i < nmailer; i++) { + if (strcmp(&buf[1], indomtab[MAILER_INDOM].it_set[i].i_name) == 0) + break; + } + if (i == nmailer) { + indomtab[MAILER_INDOM].it_set = (pmdaInstid *)realloc(indomtab[MAILER_INDOM].it_set, (nmailer+1) * sizeof(pmdaInstid)); + indomtab[MAILER_INDOM].it_set[nmailer].i_name = strdup(&buf[1]); + indomtab[MAILER_INDOM].it_set[nmailer].i_inst = nmailer; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "sendmail.cf[%d]: mailer \"%s\" inst=%d\n", + lineno, &buf[1], nmailer); +#endif + nmailer++; + } + } + else if (*bp == 'O') { + char *tp; + + if (strncasecmp(++bp, " StatusFile", 11) == 0 && + !isalnum((int)bp[11])) { + bp = strchr(bp, '='); + if (bp == NULL) + continue; + while (isspace((int)*++bp)) + continue; + } + else if (*bp == 'S') + bp++; + else + continue; + + tp = bp++; + while (*bp && !isspace((int)*bp) && *bp != '#') + bp++; + *bp = '\0'; + + statsfile = strdup(tp); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "sendmail.cf[%d]: statsfile \"%s\"\n", + lineno, tp); +#endif + } + } + fclose(fp); + + indomtab[MAILER_INDOM].it_numinst = nmailer; +} + +/* + * callback provided to pmdaFetch + */ +static int +sendmail_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (ptr == NULL) + return 0; + + if (idp->cluster == 0) { + if (idp->item == 0) { + /* sendmail.start_date */ + atom->cp = ctime(start_date); + atom->cp[24] = '\0'; /* no newline */ + return 1; + } + } + else if (idp->cluster == 1) { + if (inst >= nmailer) + return 0; + + if (msgs_from[inst] == 0 && msgs_to[inst] == 0) { + return 0; + } + + switch (idp->item) { + case 0: /* sendmail.permailer.msgs_from */ + atom->ul = msgs_from[inst]; + break; + + case 1: /* sendmail.permailer.bytes_from */ + atom->ul = kbytes_from[inst]; + break; + + case 2: /* sendmail.permailer.msgs_to */ + atom->ul = msgs_to[inst]; + break; + + case 3: /* sendmail.permailer.bytes_to */ + atom->ul = kbytes_to[inst]; + break; + + default: + return PM_ERR_PMID; + } + + return 1; + } + else if (idp->cluster == 2) { + int i; + + atom->ul = 0; + + switch (idp->item) { + case 0: /* sendmail.total.msgs_from */ + for (i = 0; i < nmailer; i++) + atom->ul += msgs_from[i]; + break; + + case 1: /* sendmail.total.bytes_from */ + for (i = 0; i < nmailer; i++) + atom->ul += kbytes_from[i]; + break; + + case 2: /* sendmail.total.msgs_to */ + for (i = 0; i < nmailer; i++) + atom->ul += msgs_to[i]; + break; + + case 3: /* sendmail.total.bytes_to */ + for (i = 0; i < nmailer; i++) + atom->ul += kbytes_to[i]; + break; + + default: + return PM_ERR_PMID; + } + + return 1; + } + + return PM_ERR_PMID; +} + +static int +sendmail_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + map_stats(); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * Initialise the agent + */ +void +__PMDA_INIT_CALL +sendmail_init(pmdaInterface *dp) +{ + if (dp->status != 0) + return; + + if (username) + __pmSetProcessIdentity(username); + + do_sendmail_cf(); + map_stats(); + + dp->version.two.fetch = sendmail_fetch; + + pmdaSetFetchCallBack(dp, sendmail_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "sendmail" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_3, pmProgname, SENDMAIL, + "sendmail.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + sendmail_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/shping/GNUmakefile b/src/pmdas/shping/GNUmakefile new file mode 100644 index 0000000..73b88da --- /dev/null +++ b/src/pmdas/shping/GNUmakefile @@ -0,0 +1,69 @@ +# +# Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = shping +DOMAIN = SHPING + +CMDTARGETS = $(IAM)$(EXECSUFFIX) +HFILES = shping.h +CFILES = shping.c pmda.c +OTHERS = README root help pmns sample.conf +CHARTS = shping.CPUTime.pmchart shping.RealTime.pmchart +PMIECS = shping.status.pmie shping.response.pmie + +LSRCFILES = $(OTHERS) $(CHARTS) $(PMIECS) Install Remove pmlogconf.summary +LLDLIBS = $(PCP_PMDALIB) $(LIB_FOR_PTHREADS) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMCHART = $(PCP_VAR_DIR)/config/pmchart +PMIE = $(PCP_VAR_DIR)/config/pmieconf/$(IAM) +LDIRT = *.log *.dir *.pag so_locations a.out domain.h $(CMDTARGETS) + +default: build-me + +include $(TOPDIR)/src/include/buildrules + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(CMDTARGETS) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(OTHERS) domain.h $(PMDADIR) + $(INSTALL) -m 644 shping.CPUTime.pmchart $(PMCHART)/shping.CPUTime + $(INSTALL) -m 644 shping.RealTime.pmchart $(PMCHART)/shping.RealTime + $(INSTALL) -m 755 -d $(PMIE) + $(INSTALL) -m 644 shping.status.pmie $(PMIE)/status + $(INSTALL) -m 644 shping.response.pmie $(PMIE)/response + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.summary $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/summary +else +build-me: +install: +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +shping.o pmda.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/shping/Install b/src/pmdas/shping/Install new file mode 100755 index 0000000..bcca1b1 --- /dev/null +++ b/src/pmdas/shping/Install @@ -0,0 +1,222 @@ +#! /bin/sh +# +# Copyright (c) 1997,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the shping PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=shping +pmda_interface=2 +forced_restart=false + +# Do it +# +pmdaSetup + +# controls for installation procedures +# +daemon_opt=true # can install as daemon +dso_opt=false +pipe_opt=true # supports pipe IPC +socket_opt=false # force pipe IPC +check_delay=10 # give the PMDA a chance to set itself up + +# be careful that mortals cannot write any configuration files, as +# these would present a security problem +# +umask 022 + + +# PMDA variables +# +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +configfile="" +cycle=120 +timeout=20 +debug=0 + +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +_quit() +{ + rm -rf $tmp + exit $1 +} + +do_debug=false + +_parsedefaults() +{ + echo "Extracting options from current installation ..." + while getopts D:I:d:l:t: c + do + case $c in + \?) echo "Warning: Unrecognized option in $PCP_PMCDCONF_PATH" + echo " Remove line for the $iam PMDA in $PCP_PMCDCONF_PATH and re-run ./Install" + _quit 2;; + D ) debug=$OPTARG;; + I ) cycle=$OPTARG;; + t ) timeout=$OPTARG;; + * ) ;; + esac + done + eval configfile='$'$OPTIND +} + +if $do_pmda +then + + # set options from $PCP_PMCDCONF_PATH, if possible + # + ans=`$PCP_AWK_PROG <$PCP_PMCDCONF_PATH ' +$1 == "'$iam'" { printf "%s",$6 + for (i=7;i<=NF;i++) printf " %s",$i + print "" + }'` + if [ ! -z "$ans" ] + then + _parsedefaults $ans + fi + + default_configfile=./sample.conf + if fgrep "CONFIGURE-ME-PLEASE" $default_configfile >/dev/null + then + # nslookup(1) may be hiding, like for OpenIndiana + # + export PATH=$PATH:/usr/sbin + nslookup=`which nslookup 2>/dev/null` + if [ -z "$nslookup" ] + then + echo "Warning: cannot find nslookup" + nslookup=nslookup + fi + # sample configuration file needs a little customization + # + if [ -f /etc/resolv.conf ] + then + my_dns_server=`$PCP_AWK_PROG $tmp/tmp \ + -e '/CONFIGURE-ME-PLEASE/d' \ + -e "s@DEFAULT-DNS-SERVER@$my_dns_server@" \ + -e "s@NSLOOKUP@$nslookup@" + cp $tmp/tmp $default_configfile + fi + + # go figure out which configuration file to use ... + # + pmdaChooseConfigFile + + if [ ! -f "$configfile" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to enter commands to create a new configuration file? [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = "Xy" -o "X$ans" = "XY" -o -z "$ans" ] + then + configfile="$configdir/$iam.conf" + if [ -f $configfile ] + then + echo "Removing old configuration file \"$configfile\"" + rm -f $configfile + if [ -f $configfile ] + then + echo "Cannot remove \"$configfile\"" + _quit 1 + fi + fi + + echo + echo \ +'Enter one ping specification per line, in the format + +tag command line details + +where the "tag" is a single unique word (no spaces) and the "command line +details" are the corresponding sh(1) command. For example + +dns-self nslookup `hostname` + +An empty line terminates the specification process and there must be at +least one specification. +' + + args="" + touch $configfile + if [ ! -f $configfile ] + then + echo "Installation aborted." + _quit 1 + fi + + while [ ! -s "$configfile" ] + do + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N "Tag Command: ""$PCP_ECHO_C" + read tag cmd + [ -z "$tag" ] && break + if grep "^$tag " $configfile >/dev/null + then + echo "Sorry, tag \"$tag\" already in use. Please try again." + continue + fi + echo "$tag $cmd" >>$configfile + done + done + else + echo "" + echo "Error: Abandoning installation as no configuration file was specified." + _quit 1 + fi + fi + + echo + echo "All commands are run one after another as a group and the group is run" + $PCP_ECHO_PROG $PCP_ECHO_N "once per \"cycle\" time. Enter the cycle time in seconds [$cycle] ""$PCP_ECHO_C" + read ans + if [ ! -z "$ans" ] + then + cycle=$ans + fi + + echo + echo "Each command must complete within a timeout period, or it will be aborted" + $PCP_ECHO_PROG $PCP_ECHO_N "by the \"$iam\" PMDA. Enter the timeout period (in seconds) [$timeout] ""$PCP_ECHO_C" + read ans + if [ ! -z "$ans" ] + then + timeout=$ans + fi + + if [ "$do_debug" = true ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Enter the debugging flag (see pmdbg(1)) [$debug] ""$PCP_ECHO_C" + read ans + if [ ! -z "$ans" ] + then + debug=$ans + fi + fi + + args="-I $cycle -t $timeout -D $debug $configfile" +fi + +pmdaInstall + +_quit 0 diff --git a/src/pmdas/shping/README b/src/pmdas/shping/README new file mode 100644 index 0000000..a071cbc --- /dev/null +++ b/src/pmdas/shping/README @@ -0,0 +1,91 @@ +Performance Co-Pilot shping PMDA for General Performance Monitoring +=================================================================== + +This PMDA is designed to be configurable to monitor elapsed time and +CPU time (user and system) for arbitrary applications that can be run +from the Bourne shell. Each application is assumed to run to completion +to probe or ping a particular service or dimension of system performance. + +The metrics exported from the shping PMDA may be used to quantify of +service or service availability for both critical system services and +tasks that well correlated to performance as perceived by end-users. + +The sample configuration file includes examples to "ping": + + + sh(1) start up and exit + + a simple task, date(1) + + sum(1) for some simple user-mode computation + + compilation and execution of an antipodean variant of the + generic "hullo world" C program + + DNS (default server, trivial and error cases) + + yp service via ypcat(1) + + rpcinfo(1) for RPC registration from portmap/rpcbind + + mail delivery (telnet tcp port 25) + + Usenet news from nntp (telnet tcp port 119) + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT shping + +Installation of the shping PMDA +=============================== + + + # cd $PCP_PMDAS_DIR/shping + + + Check that there is no clash with the Performance Metrics Domain + number defined in ./domain.h and the other PMDAs currently in use + (see $PCP_PMCDCONF_PATH). If there is, edit ./domain.h and choose + another domain number. + + + Then run the Install script (as root) + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + Answer the questions, which include the option to specify new or + alternate commands to be run. See $PCP_PMDAS_DIR/shping/sample.conf + for example specifications of commands. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/shping + # ./Remove + +Changing the settings +===================== + +The cycle time and timeout period can be dynamically modified using +pmstore(1) for the metrics shping.control.cycletime and +shping.control.timeout respectively. + +To make permanent changes, re-run the Install script. + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/shping.log) should be checked for any warnings + or errors. + + + If the Install script reports some warnings when checking the + metrics, the problem should be listed in one of the log files. + + + Additional information can be logged if there appears to be + problems. The PCP application debug flags will cause the PMDA to + report additional information in $PCP_LOG_DIR/pmcd/shping.log. For + details about the agent's debug flags, use the comand + + $ pminfo -T shping.control.debug diff --git a/src/pmdas/shping/Remove b/src/pmdas/shping/Remove new file mode 100644 index 0000000..b991ac5 --- /dev/null +++ b/src/pmdas/shping/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the shping PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=shping + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/shping/help b/src/pmdas/shping/help new file mode 100644 index 0000000..69d758c --- /dev/null +++ b/src/pmdas/shping/help @@ -0,0 +1,137 @@ +# +# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# shping PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ 19.0 shping command instance domain +There is one instance for each command run by the shping PMDA. + +The external instance name comes from the "tag" in the shping PMDA +configuration file. The internal instance number is the ordinal +command number in the configuration file. + +@ shping.error command execution error code for shping PMDA +As each command is executed, if there is a problem, the error +code or cause is stored in shping.error. + +The interpretation of the value for shping.error depends on +shping.status as follows: + + If shping.status is 1 (the command was run but returned a non-zero + exit status) then shping.error is the exit status. + + If shping.status is 2 (the command was run but was terminated by + a signal) then shping.error is the signal number. + + If shping.status is 3 (the command did not complete) then + shping.error is a PCP error codes: see pmerr(1). Of particular + relevance is -1008 (PM_ERR_TIMEOUT) when the command failed to + complete in the time specified by shping.control.timeout. + + If shping.status is 4 (the commands was not run) then shping.error + is the value of the system error code. + + Otherwise shping.error will be zero. + +@ shping.status command execution status for shping PMDA +As each command is executed, the success or failure is encoded in +shping.status, using the following values: + + -1 PMDA is initializing and command has not been run yet + 0 command completed and exit status was 0 + 1 command completed and exit status was non-zero + 2 command was run but terminated by a signal + 3 command was run but did not complete (usually a timeout) + 4 command was not run due to some system error or resource + availability + +@ shping.time.real elapsed time for a command +This metric records the elapsed time in milliseconds for the most recent +execution of each command to be run by the shping PMDA. + +Care should be used when interpreting the value if the corresponding +value for shping.status is non-zero, as the command may not have run to +completion. If the command timed out, shping.time.real will be -1. + +@ shping.time.cpu_usr user mode CPU time for a command +This metric records the user mode CPU time in milliseconds for the most +recent execution of each command to be run by the shping PMDA. + +Care should be used when interpreting the value if the corresponding +value for shping.status is non-zero, as the command may not have run to +completion. If the command timed out, shping.time.cpu_usr will be -1. + +@ shping.time.cpu_sys system mode CPU time for a command +This metric records the system mode CPU time in milliseconds for the most +recent execution of each command to be run by the shping PMDA. + +Care should be used when interpreting the value if the corresponding +value for shping.status is non-zero, as the command may not have run to +completion. If the command timed out, shping.time.cpu_sys will be -1. + +@ shping.cmd commands run by shping PMDA +The text of each sh(1) command run by the shping PMDA. + +@ shping.control.numcmd number of commands in the group to be run by the shping PMDA + +@ shping.control.cycles number of times the command group has been run by the shping PMDA + +@ shping.control.cycletime shping PMDA cycle time +All commands are run by the shping PMDA are executed one after another +in a group, and the group is run once per "cycle" time. This metric +reports the cycle time in seconds. + +The cycle time may be changed dynamically by modifying this metric +with pmstore(1). + +@ shping.control.timeout shping PMDA timeout period +The number of seconds the shping PMDA is willing to wait before +considering a single command to have timed out and killing it off. + +The time out interval may be changed dynamically by modifying this +metric with pmstore(1). + +@ shping.control.debug shping PMDA debug flag +The debug flag for the shping PMDA (see pmdbg(1)). All trace and +diagnostic files are created in $PCP_LOG_DIR/pmcd. + +The debug flags DBG_TRACE_APPL0 (2048) and DBG_TRACE_APPL1 (4096) +may be used as follows: + +DBG_TRACE_APPL0 - additional trace messages associated with the running + of each command appear in shping.log + +DBG_TRACE_APPL1 - the standard output and standard error of each command + is appended to shping.out (instead of the default + /dev/null) + +The debug flags may be changed dynamically by modifying this +metric with pmstore(1), e.g. + $ pmstore shping.control.debug 6144 +would enable both of the diagnostic traces associated with +DBG_TRACE_APPL0 and DBG_TRACE_APPL1. + diff --git a/src/pmdas/shping/pmda.c b/src/pmdas/shping/pmda.c new file mode 100644 index 0000000..648cec3 --- /dev/null +++ b/src/pmdas/shping/pmda.c @@ -0,0 +1,257 @@ +/* + * sh(1) ping PMDA + * + * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "shping.h" +#include "domain.h" + +__uint32_t cycletime = 120; /* default cycle time, 2 minutes */ +__uint32_t timeout = 20; /* response timeout in seconds */ +static char *username; /* user account to run under */ +cmd_t *cmdlist; + +#ifdef HAVE_SPROC + +/* Signals are only used with sproc, threads will never generate SIGGHLD */ +void +onchld(int s) +{ + int done; + int waitStatus; + int die = 0; + + while ((done = waitpid(-1, &waitStatus, WNOHANG)) > 0) { + + if (done != sprocpid) + continue; + die = 1; + + if (WIFEXITED(waitStatus)) { + + if (WEXITSTATUS(waitStatus) == 0) + logmessage(LOG_INFO, + "Sproc (pid=%d) exited normally\n", + done); + else + logmessage(LOG_CRIT, + "Sproc (pid=%d) exited with status = %d\n", + done, WEXITSTATUS(waitStatus)); + } + else if (WIFSIGNALED(waitStatus)) { + + if (WCOREDUMP(waitStatus)) + logmessage(LOG_CRIT, + "Sproc (pid=%d) received signal = %d and dumped core\n", + done, WTERMSIG(waitStatus)); + else + logmessage(LOG_CRIT, + "Sproc (pid=%d) received signal = %d\n", + done, WTERMSIG(waitStatus)); + } + else { + logmessage(LOG_CRIT, + "Sproc (pid=%d) died, reason unknown\n", done); + } + } + + if (die) { + logmessage(LOG_INFO, "Main process exiting\n"); + exit(0); + } +} +#endif + +void +usage(void) +{ + fprintf(stderr, +"Usage: %s [options] configfile\n\n\ +Options:\n\ + -C parse configuration file and exit\n\ + -d domain use domain (numeric) for metrics domain of PMDA\n\ + -I interval cycle time in seconds between subsequent executions of each\n\ + command [default 120 seconds]\n\ + -l logfile write log into logfile rather than using the default log\n\ + -t timeout time in seconds before aborting the wait for individual\n\ + commands to complete [default 20 seconds]\n\ + -U username run the agent and commands as alternate user [default \"pcp\"]\n", + pmProgname); + exit(1); +} + +int +main(int argc, char **argv) +{ + pmdaInterface dispatch; + int n = 0; + int i; + int err = 0; + int sep = __pmPathSeparator(); + int line; + int numcmd = 0; + int parseonly = 0; + char *configfile; + FILE *conf; + char *endnum; + char *p; + char *tag; + char lbuf[256]; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "shping" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, SHPING, + "shping.log", mypath); + + while ((n = pmdaGetOpt(argc, argv,"CD:d:I:l:t:U:?", + &dispatch, &err)) != EOF) { + switch (n) { + + case 'C': + parseonly = 1; + break; + + case 'I': + cycletime = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, + "%s: -I requires number of seconds as argument\n", + pmProgname); + err++; + } + break; + + case 't': + timeout = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, + "%s: -t requires number of seconds as argument\n", + pmProgname); + err++; + } + break; + + case 'U': + username = optarg; + break; + + case '?': + err++; + } + } + + if (err || optind != argc -1) { + usage(); + } + + configfile = argv[optind]; + if ((conf = fopen(configfile, "r")) == NULL) { + fprintf(stderr, "%s: Unable to open config file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + line = 0; + + for ( ; ; ) { + if (fgets(lbuf, sizeof(lbuf), conf) == NULL) + break; + + line++; + p = lbuf; + while (*p && isspace((int)*p)) + p++; + if (*p == '\0' || *p == '\n' || *p == '#') + continue; + tag = p++; + while (*p && !isspace((int)*p)) + p++; + if (*p) + *p++ = '\0'; + while (*p && isspace((int)*p)) + p++; + if (*p == '\0' || *p == '\n') { + fprintf(stderr, "[%s:%d] missing command after tag \"%s\"\n", + configfile, line, tag); + exit(1); + } + + numcmd++; + if (parseonly) + continue; + if ((cmdlist = (cmd_t *)realloc(cmdlist, numcmd * sizeof(cmd_t))) == NULL) { + __pmNoMem("main:cmdlist", numcmd * sizeof(cmd_t), + PM_FATAL_ERR); + } + + cmdlist[numcmd-1].tag = strdup(tag); + cmdlist[numcmd-1].cmd = strdup(p); + cmdlist[numcmd-1].status = STATUS_NOTYET; + cmdlist[numcmd-1].error = 0; + cmdlist[numcmd-1].real = cmdlist[numcmd-1].usr = cmdlist[numcmd-1].sys = -1; + + /* trim trailing newline */ + p = cmdlist[numcmd-1].cmd; + while (*p && *p != '\n') + p++; + *p = '\0'; + } + + fclose(conf); + + if (numcmd == 0) { + fprintf(stderr, "%s: No commands in config file \"%s\"?\n", + pmProgname, configfile); + exit(1); + } + else if (parseonly) + exit(0); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "Parsed %d commands\n", numcmd); + fprintf(stderr, "Tag\tCommand\n"); + for (i = 0; i < numcmd; i++) { + fprintf(stderr, "[%s]\t%s\n", cmdlist[i].tag, cmdlist[i].cmd); + } + } +#endif + + /* set up indom description */ + indomtab.it_numinst = numcmd; + if ((indomtab.it_set = (pmdaInstid *)malloc(numcmd*sizeof(pmdaInstid))) == NULL) { + __pmNoMem("main.indomtab", numcmd * sizeof(pmdaInstid), PM_FATAL_ERR); + } + for (i = 0; i < numcmd; i++) { + indomtab.it_set[i].i_inst = i; + indomtab.it_set[i].i_name = cmdlist[i].tag; + } + +#ifdef HAVE_SPROC + signal(SIGCHLD, onchld); +#endif + + pmdaOpenLog(&dispatch); + __pmSetProcessIdentity(username); + + shping_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/shping/pmlogconf.summary b/src/pmdas/shping/pmlogconf.summary new file mode 100644 index 0000000..709865e --- /dev/null +++ b/src/pmdas/shping/pmlogconf.summary @@ -0,0 +1,6 @@ +#pmlogconf-setup 2.0 +ident shping PMDA summary information +probe shping.status exists ? include : exclude + shping.status + shping.time + shping.error diff --git a/src/pmdas/shping/pmns b/src/pmdas/shping/pmns new file mode 100644 index 0000000..70003e6 --- /dev/null +++ b/src/pmdas/shping/pmns @@ -0,0 +1,41 @@ +/* + * Metrics for shping PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +shping { + status SHPING:0:6 + error SHPING:0:10 + time + cmd SHPING:0:7 + control +} + +shping.time { + real SHPING:0:0 + cpu_usr SHPING:0:1 + cpu_sys SHPING:0:2 +} + +shping.control { + numcmd SHPING:0:3 + cycles SHPING:0:9 + cycletime SHPING:0:4 + timeout SHPING:0:5 + debug SHPING:0:8 +} diff --git a/src/pmdas/shping/root b/src/pmdas/shping/root new file mode 100644 index 0000000..a0e1b52 --- /dev/null +++ b/src/pmdas/shping/root @@ -0,0 +1,9 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { shping } + +#include "pmns" diff --git a/src/pmdas/shping/sample.conf b/src/pmdas/shping/sample.conf new file mode 100644 index 0000000..028a196 --- /dev/null +++ b/src/pmdas/shping/sample.conf @@ -0,0 +1,46 @@ +# sample configuration file for shping PMDA +# +# Warning: these commands will be run as "root" using sh(1) with the +# current directory set to $PCP_LOG_DIR/pmcd and the +# following set in the environment: +# IFS=" \t\n" +# PATH=... as per $PCP_DIR/etc/pcp.env ... +# + +# Specification format, one per line ("Tags" must be unique) +# Tag Shell command + +# minimal effort here, no stress for the shell ... just start and quit +null exit 0 + +# not too much work, date(1) is pretty light-weight +date date + +# compile and run the generic simple program ... requires a C compiler +# to be installed +cc cd /tmp; rm -f $$.[oc] $$; echo "main(){printf(\"g'day world\\\\n\");}" >/tmp/$$.c; cc -o $$ $$.c; ./$$; rm -f $$.[oc] $$ + +# Is the default DNS server responding? +# CONFIGURE-ME-PLEASE - local customization required +# CONFIGURE-ME-PLEASE - DEFAULT-DNS-SERVER will be changed by Install to +# CONFIGURE-ME-PLEASE - be the hostname for the default DNS server +# CONFIGURE-ME-PLEASE - and NSLOOKUP is the path of the nslookup(1) command +dns NSLOOKUP - DEFAULT-DNS-SERVER +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "shping.h" +#include "domain.h" +#include +#if defined(HAVE_SYS_WAIT_H) +#include +#endif +#if defined(HAVE_SYS_RESOURCE_H) +#include +#endif +#if defined(HAVE_SYS_PRCTL_H) +#include +#endif +#if defined(HAVE_SCHED_H) +#include +#endif +#if defined(HAVE_PTHREAD_H) +#include +#endif + +#define LOG_PRI(p) ((p) & LOG_PRIMASK) + +static int cycles; /* completed cycles */ +static int numskip; /* skipped cycles */ +static int numcmd; /* number of commands */ +static int timedout; /* command timed out */ +static pid_t shpid; /* for /sbin/sh running command */ + +#if defined(HAVE_PTHREAD_H) +static pthread_t sprocpid; +#elif defined(HAVE_SCHED_H) +pid_t sprocpid; /* for refresh() */ +#else +#error "Need pthreads or sproc" +#endif + +/* + * only one instance domain here ... + */ +#define SHPING_INDOM 0 +pmdaIndom indomtab = { 0, 0, NULL }; + +/* + * all metrics supported in this PMDA - one table entry for each + */ +static pmdaMetric metrics[] = +{ +/* time.real */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_FLOAT, SHPING_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0), }, }, +/* time.cpu_usr */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_FLOAT, SHPING_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0), }, }, +/* time.cpu_sys */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_FLOAT, SHPING_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_MSEC,0), }, }, +/* control.numcmd */ + { NULL, + { PMDA_PMID(0,3),PM_TYPE_U32,PM_INDOM_NULL,PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0), }, }, +/* control.cycletime */ + { NULL, + { PMDA_PMID(0,4),PM_TYPE_U32,PM_INDOM_NULL,PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0), }, }, +/* control.timeout */ + { NULL, + { PMDA_PMID(0,5),PM_TYPE_U32,PM_INDOM_NULL,PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0), }, }, +/* status */ + { NULL, + { PMDA_PMID(0,6),PM_TYPE_32,SHPING_INDOM,PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0), }, }, +/* cmd */ + { NULL, + { PMDA_PMID(0,7),PM_TYPE_STRING,SHPING_INDOM,PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0), }, }, +/* control.debug */ + { NULL, + { PMDA_PMID(0,8),PM_TYPE_32,PM_INDOM_NULL,PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0), }, }, +/* control.cycles */ + { NULL, + { PMDA_PMID(0,9),PM_TYPE_U32,PM_INDOM_NULL,PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE), }, }, +/* error */ + { NULL, + { PMDA_PMID(0,10),PM_TYPE_32,SHPING_INDOM,PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0), }, }, + +}; + +static int nummetric = (sizeof(metrics)/sizeof(metrics[0])); + +void +logmessage(int priority, const char *format, ...) +{ + va_list arglist; + char buffer[2048]; + char *level; + char *p; + time_t now; + + buffer[0] = '\0'; + time(&now); + + priority = LOG_PRI(priority); + switch (priority) { + case LOG_EMERG : + level = "Emergency"; + break; + case LOG_ALERT : + level = "Alert"; + break; + case LOG_CRIT : + level = "Critical"; + break; + case LOG_ERR : + level = "Error"; + break; + case LOG_WARNING : + level = "Warning"; + break; + case LOG_NOTICE : + level = "Notice"; + break; + case LOG_INFO : + level = "Info"; + break; + case LOG_DEBUG : + level = "Debug"; + break; + default: + level = "???"; + break; + } + + va_start (arglist, format); + vsnprintf (buffer, sizeof(buffer), format, arglist); + + /* strip unwanted '\n' at end of text so's not to double up */ + for (p = buffer; *p; p++); + if (*(--p) == '\n') *p = '\0'; + + fprintf (stderr, "[%.19s] %s(%" FMT_PID ") %s: %s\n", ctime(&now), pmProgname, getpid(), level, buffer) ; + va_end (arglist) ; +} + + +static void +onhup(int dummy) +{ + _exit(0); +} + +static void +onalarm(int dummy) +{ + timedout = 1; + if (shpid > 1) { /* something other than error, self or init */ + kill(-shpid, SIGTERM); /* nuke process group */ + sleep(1); + kill(-shpid, SIGKILL); + } + signal(SIGALRM, onalarm); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "Timeout!\n"); +#endif +} + +/* + * the sproc starts here to refresh the metric values periodically + */ +static void +refresh(void *dummy) +{ + int i; + int sts; + int waittime; + struct timeval startcycle; + struct timeval now; + struct timeval then; + struct rusage cpu_now; + struct rusage cpu_then; + char *argv[4]; + + signal(SIGHUP, onhup); +#if defined (PR_TERMCHILD) + prctl(PR_TERMCHILD); /* SIGHUP when the parent dies */ +#elif defined (PR_SET_PDEATHSIG) + prctl(PR_SET_PDEATHSIG, SIGTERM); +#elif defined(IS_SOLARIS) || defined(IS_DARWIN) || defined(IS_MINGW) || defined(IS_AIX) || defined(IS_FREEBSD) || defined(IS_GNU) || defined(IS_NETBSD) + /* no signals here for child exit */ +#else +!bozo: cant decide between PR_TERMCHILD and PR_SET_PDEATHSIG +#endif +#ifndef NOFILE +#define NOFILE 7 +#endif + + signal(SIGALRM, onalarm); + + putenv("IFS= \t\n"); + putenv("PATH=/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin"); + + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = ""; + argv[3] = NULL; + + for ( ; ; ) { + cycles++; + __pmtimevalNow(&startcycle); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "\nStart cycle @ %s", ctime(&startcycle.tv_sec)); +#endif + for (i = 0; i < numcmd; i++) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "[%s] %s ->\n", cmdlist[i].tag, cmdlist[i].cmd); +#endif + getrusage(RUSAGE_CHILDREN, &cpu_then); + __pmtimevalNow(&then); + fflush(stderr); + fflush(stdout); + shpid = fork(); + if (shpid == 0) { + int j; + setsid(); /* make new process group */ + for (j = 0; j < NOFILE; j++) + close(j); + open("/dev/null", O_RDONLY, 0); + sts = -1; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + FILE *f; + if ((f = fopen("shping.out", "a")) != NULL) { + fprintf(f, "\n[%s] %s\n", cmdlist[i].tag, cmdlist[i].cmd); + fclose(f); + } + sts = open("shping.out", O_WRONLY|O_APPEND|O_CREAT, 0644); + } +#endif + if (sts == -1) + open("/dev/null", O_WRONLY, 0); + if (dup(1) == -1) { + fprintf(stderr, "Warning: dup() failed: %s\n", pmErrStr(-oserror())); + } + argv[2] = cmdlist[i].cmd; + sts = execv("/bin/sh", argv); + exit(-1); + } + else if (shpid < 0) { + logmessage(LOG_CRIT, "refresh: fork() failed: %s", osstrerror()); + cmdlist[i].status = STATUS_SYS; + cmdlist[i].error = oserror(); + cmdlist[i].real = cmdlist[i].usr = cmdlist[i].sys = -1; + continue; + + } + timedout = 0; + alarm(timeout); + waitpid(shpid, &sts, 0); + __pmtimevalNow(&now); + getrusage(RUSAGE_CHILDREN, &cpu_now); + alarm(0); + + if (timedout) { + cmdlist[i].error = PM_ERR_TIMEOUT; + cmdlist[i].status = STATUS_TIMEOUT; + cmdlist[i].real = cmdlist[i].usr = cmdlist[i].sys = -1; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_INFO, "[%s] timeout\n", cmdlist[i].tag); +#endif + } + else { + if (WIFEXITED(sts)) { + cmdlist[i].error = WEXITSTATUS(sts); + if (cmdlist[i].error == 0) + cmdlist[i].status = STATUS_OK; + else { + cmdlist[i].status = STATUS_EXIT; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_INFO, "[%s] exit status: %d\n", + cmdlist[i].tag, cmdlist[i].error); +#endif + } + } + else if (WIFSIGNALED(sts)) { + cmdlist[i].error = WTERMSIG(sts); + cmdlist[i].status = STATUS_SIG; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_INFO, "[%s] killed signal: %d\n", + cmdlist[i].tag, cmdlist[i].error); +#endif + } + else { + /* assume WIFSTOPPED(sts) */ + cmdlist[i].error = WSTOPSIG(sts); + cmdlist[i].status = STATUS_SIG; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_INFO, "[%s] stopped signal: %d\n", + cmdlist[i].tag, cmdlist[i].error); +#endif + } + cmdlist[i].real = 1000 * (float)(now.tv_sec - then.tv_sec) + + (float)(now.tv_usec - then.tv_usec) / 1000; + cmdlist[i].usr = 1000 * (float)(cpu_now.ru_utime.tv_sec - cpu_then.ru_utime.tv_sec) + + (float)(cpu_now.ru_utime.tv_usec - cpu_then.ru_utime.tv_usec) / 1000; + cmdlist[i].sys = 1000 * (float)(cpu_now.ru_stime.tv_sec - cpu_then.ru_stime.tv_sec) + + (float)(cpu_now.ru_stime.tv_usec - cpu_then.ru_stime.tv_usec) / 1000; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + fprintf(stderr, "status: %d error: %d real: %.3f usr: %.3f sys: %.3f\n\n", + cmdlist[i].status, cmdlist[i].error, + cmdlist[i].real, cmdlist[i].usr, cmdlist[i].sys); +#endif + } + + /* + * harvest delinquent children ... includes those who were fork()'d + * above, and timed out. + */ + for ( ; ; ) { + sts = waitpid(-1, NULL, WNOHANG); + if (sts <= 0) + break; + } + + __pmtimevalNow(&now); + if (cycletime) { + waittime = (int)cycletime - now.tv_sec + startcycle.tv_sec; + if (waittime < 0) { + /* can't keep up */ + while (waittime < 0) { + numskip++; + waittime += cycletime; + } + } + sleep(waittime); + } + } +} + +static int +shping_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *ext) +{ + int i; /* over pmidlist[] */ + int j; /* over vset->vlist[] */ + int sts; + int need; + int inst; + int numval; + static pmResult *res = NULL; + static int maxnpmids = 0; + pmValueSet *vset; + __pmID_int *pmidp; + pmAtomValue atom; + pmDesc *dp = NULL; + int type; + +#ifndef HAVE_SPROC + /* In the pthread world we don't have asyncronous notification that + * a thread has died, so we use pthread_kill to check is refresh + * is still running. */ + int err; + + if ( (err = pthread_kill (sprocpid, 0)) != 0 ) { + logmessage (LOG_CRIT, "'refresh' thread is not responding: %s\n", + strerror (err)); + exit (1); + } +#endif + + + if (numpmid > maxnpmids) + { + if (res != NULL) + free(res); + +/* (numpmid - 1) because there's room for one valueSet in a pmResult */ + + need = sizeof(pmResult) + (numpmid - 1) * sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == NULL) + return -oserror(); + maxnpmids = numpmid; + } + + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + + for (i = 0; i < numpmid; i++) { + + pmidp = (__pmID_int*)&pmidlist[i]; + dp = NULL; + + if (ext->e_direct) { + if (pmidp->cluster == 0 && pmidp->item < nummetric) + dp = &metrics[pmidp->item].m_desc; + } + else { + for (j = 1; jcluster == 0 && + metrics[j].m_desc.pmid == pmidlist[i]) { + dp = &metrics[j].m_desc; + break; + } + } + } + + if (dp == NULL) + numval = PM_ERR_PMID; + else { + if (dp->indom != PM_INDOM_NULL) { + numval = 0; + __pmdaStartInst(dp->indom, ext); + while(__pmdaNextInst(&inst, ext)) { + numval++; + } + } + else { + numval = 1; + } + } + + if (numval >= 1) + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) + + (numval - 1)*sizeof(pmValue)); + else + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) - + sizeof(pmValue)); + + if (vset == NULL) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = numval; + vset->valfmt = PM_VAL_INSITU; + if (vset->numval <= 0) + continue; + + if (dp->indom == PM_INDOM_NULL) + inst = PM_IN_NULL; + else { + __pmdaStartInst(dp->indom, ext); + __pmdaNextInst(&inst, ext); + } + + type = dp->type; + pmidp = (__pmID_int *)&pmidlist[i]; + j = 0; + + do { + if (j == numval) { + /* more instances than expected! */ + numval++; + res->vset[i] = vset = (pmValueSet *)realloc(vset, + sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue)); + if (vset == NULL) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + } + vset->vlist[j].inst = inst; + + if (pmidp->cluster == 0) { + switch (pmidp->item) { + + case 0: /* shping.time.real PMID: ...0.0 */ + atom.f = cmdlist[inst].real; + break; + + case 1: /* shping.time.cpu_usr PMID: ...0.1 */ + atom.f = cmdlist[inst].usr; + break; + + case 2: /* shping.time.cpu_sys PMID: ...0.2 */ + atom.f = cmdlist[inst].sys; + break; + + case 3: /* shping.control.numcmd PMID: ...0.3 */ + atom.ul = numcmd; + break; + + case 4: /* shping.control.cycletime PMID: ...0.4 */ + atom.ul = cycletime; + break; + + case 5: /* shping.control.timeout PMID: ...0.5 */ + atom.ul = timeout; + break; + + case 6: /* shping.status PMID: ...0.6 */ + atom.l = cmdlist[inst].status; + break; + + case 7: /* shping.cmd PMID: ...0.7 */ + atom.cp = cmdlist[inst].cmd; + break; + + case 8: /* shping.control.debug PMID: ...0.8 */ + atom.l = pmDebug; + break; + + case 9: /* shping.control.cycles PMID: ...0.9 */ + atom.ul = cycles; + break; + + case 10: /* shping.error PMID: ...0.10 */ + atom.ul = cmdlist[inst].error; + break; + + default: + j = PM_ERR_PMID; + break; + } + } + if (j < 0) + break; + + sts = __pmStuffValue(&atom, &vset->vlist[j], type); + if (sts < 0) { + __pmFreeResultValues(res); + return sts; + } + + vset->valfmt = sts; + j++; /* next element in vlist[] for next instance */ + } while (dp->indom != PM_INDOM_NULL && __pmdaNextInst(&inst, ext)); + + vset->numval = j; + } + *resp = res; + return 0; +} + +static int +shping_store(pmResult *result, pmdaExt *ext) +{ + int i; + pmValueSet *vsp; + int sts = 0; + int ival; + __pmID_int *pmidp; + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + if (pmidp->cluster == 0) { + switch (pmidp->item) { + case 4: /* shping.control.cycletime PMID: ...0.4 */ + ival = vsp->vlist[0].value.lval; + if (ival < 0) { + sts = PM_ERR_SIGN; + break; + } + cycletime = ival; + break; + + case 5: /* shping.control.timeout PMID: ...0.5 */ + ival = vsp->vlist[0].value.lval; + if (ival < 0) { + sts = PM_ERR_SIGN; + break; + } + timeout = ival; + break; + + case 8: /* shping.control.debug PMID: ...0.8 */ + ival = vsp->vlist[0].value.lval; + if (ival < 0) { + sts = PM_ERR_SIGN; + break; + } + pmDebug = ival; + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else { + /* not one of the metrics we are willing to change */ + sts = PM_ERR_PMID; + break; + } + } + return sts; +} + +void +shping_init(pmdaInterface *dp) +{ + if (dp->status != 0) + return; + + unlink("shping.out"); /* just in case */ + + dp->version.two.fetch = shping_fetch; + dp->version.two.store = shping_store; + + pmdaInit(dp, &indomtab, 1, metrics, nummetric); + + if (dp->version.two.ext->e_direct == 0) { + logmessage(LOG_CRIT, "Metric tables require direct mapping.\n"); + dp->status = -1; + return; + } + + numcmd = indomtab.it_numinst; + + /* start the sproc for async fetches */ +#ifdef HAVE_SPROC + if ( (sprocpid = sproc(refresh, PR_SADDR, NULL)) < 0 ) { + logmessage(LOG_CRIT, "sproc failed: %s\n", osstrerror()); + dp->status = sprocpid; + } + else { + dp->status = 0; + logmessage(LOG_INFO, "Started sproc (spid=%" FMT_PID ")\n", sprocpid); + } + + /* we're talking to pmcd ... no timeout's for us thanks */ + signal(SIGALRM, SIG_IGN); + +#elif defined (HAVE_PTHREAD_H) + { + int err = pthread_create(&sprocpid, NULL, (void (*))refresh, NULL); + if ( err != 0 ) { + logmessage (LOG_CRIT, "Cannot spawn a new thread: %s\n", + strerror (err)); + dp->status = err; + } else { + dp->status = 0; + logmessage (LOG_INFO, "Started refresh thread\n"); + } + } +#else +#error "Need pthreads or sproc" +#endif +} diff --git a/src/pmdas/shping/shping.h b/src/pmdas/shping/shping.h new file mode 100644 index 0000000..1c266a1 --- /dev/null +++ b/src/pmdas/shping/shping.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +typedef struct { + char *tag; + char *cmd; + int status; + int error; + float real; + float usr; + float sys; +} cmd_t; + +#define STATUS_NOTYET -1 +#define STATUS_OK 0 +#define STATUS_EXIT 1 +#define STATUS_SIG 2 +#define STATUS_TIMEOUT 3 +#define STATUS_SYS 4 + +extern cmd_t *cmdlist; + +extern __uint32_t cycletime; /* seconds per command cycle */ +extern __uint32_t timeout; /* response timeout in seconds */ +#ifdef HAVE_SPROC +extern pid_t sprocpid; /* for refresh() */ +#endif +extern pmdaIndom indomtab; /* cmd tag indom */ + +extern int pmDebug; +extern char *pmProgname; + +extern void shping_init(pmdaInterface *); + +extern void logmessage(int, const char *, ...); diff --git a/src/pmdas/shping/shping.response.pmie b/src/pmdas/shping/shping.response.pmie new file mode 100644 index 0000000..f77457a --- /dev/null +++ b/src/pmdas/shping/shping.response.pmie @@ -0,0 +1,67 @@ +#pmieconf-rules 1 +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) + +rule shping.response + summary = "$rule$" + enumerate = hosts + predicate = +"some_inst ( + shping.time.real $hosts$ > $threshold$ && + shping.status $hosts$ == 0 +)" + enabled = no + version = 1 + help = +"A monitored application or service probe from the shping PMDA +has taken more than threshold milliseconds to complete. + +If this rule is enabled, the shping PMDA should be installed; see +$PCP_PMDAS_DIR/shping/README and pmdashping(1). + +If some application or service is not available then the +corresponding line should be commented out of the shping +configuration file ($PCP_PMDAS_DIR/shping/shping.conf by default) +and the shping PMDA restarted."; + +string rule + default = "Shell-ping PMDA slow application or service response" + modify = no + display = no; + +unsigned threshold + default = 3000 + help = +"Threshold time, in milliseconds, for command to run to completion."; + +string action_expand + default = %vmsec[%i] + display = no + modify = no; + +string email_expand + default = "service: %i response time: %vmsec" + display = no + modify = no; + + +# Configuration info specific to non-PCP tools follows... +# + +# for SGI Embedded Support Partner integration: +string esp_type + default = "0x200095" + display = no + modify = no; + +# for EnlightenDSM integration: +string enln_test + default = shping.response + display = no + modify = no; +string enln_units + default = msec[%i] + display = no + modify = no; + +# +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) diff --git a/src/pmdas/shping/shping.status.pmie b/src/pmdas/shping/shping.status.pmie new file mode 100644 index 0000000..199c600 --- /dev/null +++ b/src/pmdas/shping/shping.status.pmie @@ -0,0 +1,63 @@ +#pmieconf-rules 1 +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) + +rule shping.status + summary = "$rule$" + enumerate = hosts + predicate = +"some_inst ( + shping.status $hosts$ @0 > 0 && shping.status $hosts$ @1 == 0 +)" + enabled = no + version = 1 + help = +"An application or service being monitored by the shping PMDA was +previously probed successfully and the probe has either failed, or +not responded within a timeout period (as defined by the PCP metric +shping.control.timeout) during the last sample interval. + +If this rule is enabled, the shping PMDA should be installed; see +$PCP_PMDAS_DIR/shping/README and pmdashping(1). + +If some application or service is not available then the +corresponding line should be commented out of the shping +configuration file ($PCP_PMDAS_DIR/shping/shping.conf by default) +and the shping PMDA restarted."; + +string rule + default = "Shell-ping PMDA application or service probe failure" + modify = no + display = no; + +string action_expand + default = %i + display = no + modify = no; + +string email_expand + default = "application or service: %i" + display = no + modify = no; + + +# Configuration info specific to non-PCP tools follows... +# + +# for SGI Embedded Support Partner integration: +string esp_type + default = "0x200090" + display = no + modify = no; + +# for EnlightenDSM integration: +string enln_test + default = shping.status + display = no + modify = no; +string enln_units + default = exit_status + display = no + modify = no; + +# +# --- DO NOT MODIFY THIS FILE --- see pmieconf(4) diff --git a/src/pmdas/simple/GNUmakefile b/src/pmdas/simple/GNUmakefile new file mode 100644 index 0000000..a0c50bb --- /dev/null +++ b/src/pmdas/simple/GNUmakefile @@ -0,0 +1,46 @@ +# +# Copyright (c) 2012-2013 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +CFILES = simple.c +CMDTARGET = pmdasimple$(EXECSUFFIX) +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = -I. +DFILES = README help +SCRIPTS = pmdasimple.perl pmdasimple.python +LSRCFILES = Install Remove pmns root $(DFILES) $(SCRIPTS) \ + simple.conf GNUmakefile.install + +IAM = simple +DOMAIN = SIMPLE +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so + +default_pcp default: domain.h $(CMDTARGET) + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 GNUmakefile.install $(PMDADIR)/Makefile + $(INSTALL) -m 644 root pmns domain.h simple.conf $(PMDADIR) + $(INSTALL) -m 644 $(CFILES) $(DFILES) $(SCRIPTS) $(PMDADIR) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/simple/GNUmakefile.install b/src/pmdas/simple/GNUmakefile.install new file mode 100644 index 0000000..2df8bb4 --- /dev/null +++ b/src/pmdas/simple/GNUmakefile.install @@ -0,0 +1,53 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +SHELL = sh + +ifdef PCP_CONF +include $(PCP_CONF) +else +PCP_DIR = $(shell echo $$PCP_DIR) +include $(PCP_DIR)/etc/pcp.conf +endif +include $(PCP_INC_DIR)/builddefs + +# remove -Lpath and -Ipath options from builddefs CFLAGS value +# +PCP_LIBS = +TMP := $(CFLAGS:-I%=) +ifdef PCP_DIR +# put -Ipath and -Lpath back but use paths for run-time environment +# +CFLAGS = $(TMP) -I$(PCP_INC_DIR)/.. +LDFLAGS = -L$(PCP_LIB_DIR) +else +CFLAGS = $(TMP) +endif + +IAM = simple +CFILES = $(IAM).c + +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +CMDTARGET = pmda$(IAM) +TARGETS = $(LIBTARGET) $(CMDTARGET) + +LLDLIBS = -lpcp_pmda -lpcp $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) +LDIRT = *.log help.dir help.pag + +default: $(TARGETS) + +install: default + +include $(PCP_INC_DIR)/buildrules diff --git a/src/pmdas/simple/Install b/src/pmdas/simple/Install new file mode 100644 index 0000000..0d3ead3 --- /dev/null +++ b/src/pmdas/simple/Install @@ -0,0 +1,53 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the simple PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=simple +pmda_interface=2 +forced_restart=false + +dso_opt=true +perl_opt=true +python_opt=true +socket_opt=true +socket_inet_def=2078 + +# Set up the simple PMDA (domain 253) InDom cache +# +domain=`sed -n 255) { $sts = PM_ERR_CONV; $val = 255; } + + if ($inst == 0) { $red = $val; } + elsif ($inst == 1) { $green = $val; } + elsif ($inst == 2) { $blue = $val; } + else { $sts = PM_ERR_INST; } + } + else { $sts = PM_ERR_PMID; } + return $sts; + } + elsif ( ($cluster == 1 && ($item == 2 || $item == 3)) + || ($cluster == 2 && $item == 4) ) { + return PM_ERR_PERMISSION; + } + return PM_ERR_PMID; +} + +sub simple_timenow_check +{ + %timeslices = (); + + if (open(CONFIG, $simple_config)) { + my %values; + + ($values{'sec'}, $values{'min'}, $values{'hour'}, + undef,undef,undef,undef,undef) = localtime; + $_ = ; + chomp; # avoid possible \n on last field + foreach my $spec (split(/,/)) { + $timeslices{$spec} = $values{$spec}; + } + close CONFIG; + $file_error = 0; + } + else { + unless ($file_error == $!) { + $pmda->log("read failed on $simple_config: $!"); + $file_error = $!; + } + } + $pmda->replace_indom( $now_indom, \%timeslices); +} + +$pmda = PCP::PMDA->new('simple', 253); +$pmda->connect_pmcd; + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'simple.numfetch', '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_32, $color_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'simple.color', '', ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_DOUBLE, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'simple.time.user', '', ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'simple.time.sys', '', ''); +$pmda->add_metric(pmda_pmid(2,4), PM_TYPE_U32, $now_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'simple.now', '', ''); + +$pmda->add_indom($color_indom, [0 => 'red', 1 => 'green', 2 => 'blue'], '', ''); +$now_indom = $pmda->add_indom($now_indom, {}, '', ''); # initialized on-the-fly +$pmda->set_fetch( \&simple_fetch ); +$pmda->set_instance( \&simple_instance ); +$pmda->set_fetch_callback( \&simple_fetch_callback ); +$pmda->set_store_callback( \&simple_store_callback ); + +$pmda->set_user('pcp'); +&simple_timenow_check; +$pmda->run; diff --git a/src/pmdas/simple/pmdasimple.python b/src/pmdas/simple/pmdasimple.python new file mode 100644 index 0000000..5d0295a --- /dev/null +++ b/src/pmdas/simple/pmdasimple.python @@ -0,0 +1,244 @@ +''' +Python implementation of the "simple" Performance Metrics Domain Agent. +''' +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +import os +import time +import ctypes +from ctypes import c_int, POINTER, cast +import cpmapi as c_api +from pcp.pmda import PMDA, pmdaMetric, pmdaIndom, pmdaInstid +from pcp.pmapi import pmUnits, pmContext as PCP + +class SimplePMDA(PMDA): + ''' + A simple Performance Metrics Domain Agent with very simple metrics. + Install it and make basic use of it, as follows: + + # $PCP_PMDAS_DIR/simple/Install + [select python option] + $ pminfo -fmdtT simple + + Then experiment with the simple.conf file (see simple.now metrics). + ''' + + # simple.color instance domain + red = 0 + green = 100 + blue = 200 + colors = [pmdaInstid(0, 'red'), + pmdaInstid(1, 'green'), + pmdaInstid(2, 'blue')] + + # simple.now instance domain + configfile = '' + timeslices = {} + times = () + + # simple.numfetch properties + numfetch = 0 + oldfetch = -1 + + + def simple_instance(self, serial): + ''' Called once per "instance request" PDU ''' + # self.log("instance update for %d" % serial) + if (serial == 1): + self.simple_timenow_check() + + + def simple_fetch(self): + ''' Called once per "fetch" PDU, before callbacks ''' + self.numfetch += 1 + self.simple_timenow_check() + + def simple_fetch_color_callback(self, item, inst): + ''' + Returns a list of value,status (single pair) for color cluster + Helper for the fetch callback + ''' + if (item == 0): + if (inst != c_api.PM_IN_NULL): + return [c_api.PM_ERR_INST, 0] + return [self.numfetch, 1] + elif (item == 1): + if (inst == 0): + self.red = (self.red + 1) % 255 + return [self.red, 1] + elif (inst == 1): + self.green = (self.green + 1) % 255 + return [self.green, 1] + elif (inst == 2): + self.blue = (self.blue + 1) % 255 + return [self.blue, 1] + else: + return [c_api.PM_ERR_INST, 0] + else: + return [c_api.PM_ERR_PMID, 0] + + def simple_fetch_times_callback(self, item, inst): + ''' + Returns a list of value,status (single pair) for times cluster + Helper for the fetch callback + ''' + if (inst != c_api.PM_IN_NULL): + return [c_api.PM_ERR_INST, 0] + if (self.oldfetch < self.numfetch): # get current values, if needed + self.times = os.times() + self.oldfetch = self.numfetch + if (item == 2): + return [self.times[0], 1] + elif (item == 3): + return [self.times[1], 1] + return [c_api.PM_ERR_PMID, 0] + + def simple_fetch_now_callback(self, item, inst): + ''' + Returns a list of value,status (single pair) for "now" cluster + Helper for the fetch callback + ''' + if (item == 4): + voidp = self.inst_lookup(self.now_indom, inst) + if (voidp == None): + return [c_api.PM_ERR_INST, 0] + valuep = cast(voidp, POINTER(c_int)) + return [valuep.contents.value, 1] + return [c_api.PM_ERR_PMID, 0] + + def simple_fetch_callback(self, cluster, item, inst): + ''' + Main fetch callback, defers to helpers for each cluster. + Returns a list of value,status (single pair) for requested pmid/inst + ''' + # self.log("fetch callback for %d.%d[%d]" % (cluster, item, inst)) + if (cluster == 0): + return self.simple_fetch_color_callback(item, inst) + elif (cluster == 1): + return self.simple_fetch_times_callback(item, inst) + elif (cluster == 2): + return self.simple_fetch_now_callback(item, inst) + return [c_api.PM_ERR_PMID, 0] + + + def simple_store_count_callback(self, val): + ''' Helper for the store callback, handles simple.numfetch ''' + sts = 0 + if (val < 0): + sts = c_api.PM_ERR_SIGN + val = 0 + self.numfetch = val + return sts + + def simple_store_color_callback(self, val, inst): + ''' Helper for the store callback, handles simple.color ''' + sts = 0 + if (val < 0): + sts = c_api.PM_ERR_SIGN + val = 0 + elif (val > 255): + sts = c_api.PM_ERR_CONV + val = 255 + + if (inst == 0): + self.red = val + elif (inst == 1): + self.green = val + elif (inst == 2): + self.blue = val + else: + sts = c_api.PM_ERR_INST + return sts + + def simple_store_callback(self, cluster, item, inst, val): + ''' + Store callback, executed when a request to write to a metric happens + Defers to helpers for each storable metric. Returns a single value. + ''' + if (cluster == 0): + if (item == 0): + return self.simple_store_count_callback(val) + elif (item == 1): + return self.simple_store_color_callback(val, inst) + else: + return c_api.PM_ERR_PMID + elif ((cluster == 1 and (item == 2 or item == 3)) + or (cluster == 2 and item == 4)): + return c_api.PM_ERR_PERMISSION + return c_api.PM_ERR_PMID + + + def simple_timenow_check(self): + ''' + Read our configuration file and update instance domain + ''' + self.timeslices.clear() + try: + cfg = open(self.configfile) + fields = time.localtime() + values = {'sec': c_int(fields[5]), + 'min': c_int(fields[4]), + 'hour': c_int(fields[3])} + config = cfg.readline().strip() + for key in config.split(','): + if (key != '' and values[key] != None): + self.timeslices[key] = values[key] + finally: + cfg.close() + self.replace_indom(self.now_indom, self.timeslices) + + + def __init__(self, name, domain): + PMDA.__init__(self, name, domain) + + self.configfile = PCP.pmGetConfig('PCP_PMDAS_DIR') + self.configfile += '/' + name + '/' + name + '.conf' + + self.connect_pmcd(); + + self.color_indom = self.indom(0) + self.add_indom(pmdaIndom(self.color_indom, self.colors)) + + self.now_indom = self.indom(1) + self.add_indom(pmdaIndom(self.now_indom, self.timeslices)) + + self.add_metric(name + '.numfetch', pmdaMetric(self.pmid(0, 0), + c_api.PM_TYPE_U32, c_api.PM_INDOM_NULL, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + self.add_metric(name + '.color', pmdaMetric(self.pmid(0, 1), + c_api.PM_TYPE_32, self.color_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + self.add_metric(name + '.time.user', pmdaMetric(self.pmid(1, 2), + c_api.PM_TYPE_DOUBLE, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 1, 0, 0, c_api.PM_TIME_SEC, 0))) + self.add_metric(name + '.time.sys', pmdaMetric(self.pmid(1, 3), + c_api.PM_TYPE_DOUBLE, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 1, 0, 0, c_api.PM_TIME_SEC, 0))) + self.add_metric(name + '.now', pmdaMetric(self.pmid(2, 4), + c_api.PM_TYPE_U32, self.now_indom, c_api.PM_SEM_INSTANT, + pmUnits(0, 0, 0, 0, 0, 0))) + + self.set_fetch(self.simple_fetch) + self.set_instance(self.simple_instance) + self.set_fetch_callback(self.simple_fetch_callback) + self.set_store_callback(self.simple_store_callback) + self.set_user(PCP.pmGetConfig('PCP_USER')) + self.simple_timenow_check() + + +if __name__ == '__main__': + + SimplePMDA('simple', 253).run() + diff --git a/src/pmdas/simple/pmns b/src/pmdas/simple/pmns new file mode 100644 index 0000000..ab987f4 --- /dev/null +++ b/src/pmdas/simple/pmns @@ -0,0 +1,27 @@ +/* + * Metrics for simple PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +simple { + numfetch SIMPLE:0:0 + color SIMPLE:0:1 + time + now SIMPLE:2:4 +} + +simple.time { + user SIMPLE:1:2 + sys SIMPLE:1:3 +} diff --git a/src/pmdas/simple/root b/src/pmdas/simple/root new file mode 100644 index 0000000..d139152 --- /dev/null +++ b/src/pmdas/simple/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { simple } + +#include "pmns" + diff --git a/src/pmdas/simple/simple.c b/src/pmdas/simple/simple.c new file mode 100644 index 0000000..d0eca16 --- /dev/null +++ b/src/pmdas/simple/simple.c @@ -0,0 +1,517 @@ +/* + * Simple, configurable PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "domain.h" +#include + +/* + * Simple PMDA + * + * This PMDA is a sample that illustrates how a simple PMDA might be + * constructed using libpcp_pmda. + * + * Although the metrics supported are simple, the framework is quite general, + * and could be extended to implement a much more complex PMDA. + * + * Metrics + * simple.numfetch - number of fetches from this PMDA, + * may be re-set using pmStore + * simple.colors - 3 instances ("red", "green" and "blue") + * of a "saw-tooth" sequence + * simple.time.user - time in seconds spent executing user code + * simple.time.sys - time in seconds spent executing system code + * simple.now - current time of day across a dynamically + * re-configurable instance domain. + */ + +/* + * list of instances + */ + +static pmdaInstid color[] = { + { 0, "red" }, { 1, "green" }, { 2, "blue" } +}; + +/* + * instance domains + * COLOR_INDOM uses the classical indomtab[] method + * NOW_INDOM uses the more recent pmdaCache methods, but also appears in + * indomtab[] so that the initialization of the pmInDom and the pmDescs + * in metrictab[] is completed by pmdaInit + */ + +static pmdaIndom indomtab[] = { +#define COLOR_INDOM 0 /* serial number for "color" instance domain */ + { COLOR_INDOM, sizeof(color)/sizeof(color[0]), color }, +#define NOW_INDOM 1 /* serial number for "now" instance domain */ + { NOW_INDOM, 0, NULL }, +}; + +/* this is merely a convenience */ +static pmInDom *now_indom = &indomtab[NOW_INDOM].it_indom; + +/* + * All metrics supported in this PMDA - one table entry for each. + * The 4th field specifies the serial number of the instance domain + * for the metric, and must be either PM_INDOM_NULL (denoting a + * metric that only ever has a single value), or the serial number + * of one of the instance domains declared in the instance domain table + * (i.e. in indomtab, above). + */ + +static pmdaMetric metrictab[] = { +/* numfetch */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* color */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_32, COLOR_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* time.user */ + { NULL, + { PMDA_PMID(1,2), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) }, }, +/* time.sys */ + { NULL, + { PMDA_PMID(1,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) }, }, +/* now */ + { NULL, + { PMDA_PMID(2,4), PM_TYPE_U32, NOW_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +static int numfetch = 0; /* number of pmFetch operations */ +static int red = 0; /* current red value */ +static int green = 100; /* current green value */ +static int blue = 200; /* current blue value */ +static int isDSO = 1; /* =0 I am a daemon */ +static char *username; + +/* data and function prototypes for dynamic instance domain handling */ +static struct timeslice { + int tm_field; + int inst_id; + char *tm_name; +} timeslices[] = { + { 0, 1, "sec" }, { 0, 60, "min" }, { 0, 3600, "hour" } +}; +static int num_timeslices = sizeof(timeslices)/sizeof(timeslices[0]); + +#define SIMPLE_BUFSIZE 256 +static struct stat file_change; /* has time of last configuration change */ +static void simple_timenow_clear(void); +static void simple_timenow_init(void); +static void simple_timenow_refresh(void); +static void simple_timenow_check(void); + +static char mypath[MAXPATHLEN]; + +/* command line option handling - both short and long options */ +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_TEXT("\nExactly one of the following options may appear:"), + PMDAOPT_INET, + PMDAOPT_PIPE, + PMDAOPT_UNIX, + PMDAOPT_IPV6, + PMDA_OPTIONS_END +}; +static pmdaOptions opts = { + .short_options = "D:d:i:l:pu:U:6:?", + .long_options = longopts, +}; + +/* + * callback provided to pmdaFetch + */ +static int +simple_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + int sts; + static int oldfetch; + static double usr, sys; + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (inst != PM_IN_NULL && + !(idp->cluster == 0 && idp->item == 1) && + !(idp->cluster == 2 && idp->item == 4)) + return PM_ERR_INST; + + if (idp->cluster == 0) { + if (idp->item == 0) { /* simple.numfetch */ + atom->l = numfetch; + } + else if (idp->item == 1) { /* simple.color */ + switch (inst) { + case 0: /* red */ + red = (red + 1) % 256; + atom->l = red; + break; + case 1: /* green */ + green = (green + 1) % 256; + atom->l = green; + break; + case 2: /* blue */ + blue = (blue + 1) % 256; + atom->l = blue; + break; + default: + return PM_ERR_INST; + } + } + else + return PM_ERR_PMID; + } + else if (idp->cluster == 1) { /* simple.time */ + if (oldfetch < numfetch) { + __pmProcessRunTimes(&usr, &sys); + oldfetch = numfetch; + } + if (idp->item == 2) /* simple.time.user */ + atom->d = usr; + else if (idp->item == 3) /* simple.time.sys */ + atom->d = sys; + else + return PM_ERR_PMID; + } + else if (idp->cluster == 2) { + if (idp->item == 4) { /* simple.now */ + struct timeslice *tsp; + if ((sts = pmdaCacheLookup(*now_indom, inst, NULL, (void *)&tsp)) != PMDA_CACHE_ACTIVE) { + if (sts < 0) + __pmNotifyErr(LOG_ERR, "pmdaCacheLookup failed: inst=%d: %s", inst, pmErrStr(sts)); + return PM_ERR_INST; + } + atom->l = tsp->tm_field; + } + else + return PM_ERR_PMID; + } + else + return PM_ERR_PMID; + + return 0; +} + +/* + * wrapper for pmdaFetch which increments the fetch count and checks for + * a change to the NOW instance domain. + * + * This routine is called once for each pmFetch(3) operation, so is a + * good place to do once-per-fetch functions, such as value caching or + * instance domain evaluation (as we do in simple_timenow_check). + */ +static int +simple_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + numfetch++; + simple_timenow_check(); + simple_timenow_refresh(); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * wrapper for pmdaInstance which we need to ensure is called with the + * _current_ contents of the NOW instance domain. + */ +static int +simple_instance(pmInDom indom, int foo, char *bar, __pmInResult **iresp, pmdaExt *pmda) +{ + simple_timenow_check(); + return pmdaInstance(indom, foo, bar, iresp, pmda); +} + +/* + * Re-evaluate the NOW instance domain. + * + * Refer to the help text for simple.now for an explanation of how + * this indom can be modified, or just read the code ... + */ +static void +simple_timenow_check(void) +{ + struct stat statbuf; + static int last_error = 0; + int sep = __pmPathSeparator(); + + /* stat the file & check modification time has changed */ + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "simple.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if (stat(mypath, &statbuf) == -1) { + if (oserror() != last_error) { + last_error = oserror(); + __pmNotifyErr(LOG_ERR, "stat failed on %s: %s\n", + mypath, pmErrStr(-last_error)); + } + simple_timenow_clear(); + } + else { + last_error = 0; +#if defined(HAVE_ST_MTIME_WITH_E) + if (statbuf.st_mtime != file_change.st_mtime) { +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + if (statbuf.st_mtimespec.tv_sec != file_change.st_mtimespec.tv_sec || + statbuf.st_mtimespec.tv_nsec != file_change.st_mtimespec.tv_nsec) { +#else + if (statbuf.st_mtim.tv_sec != file_change.st_mtim.tv_sec || + statbuf.st_mtim.tv_nsec != file_change.st_mtim.tv_nsec) { +#endif + simple_timenow_clear(); + simple_timenow_init(); + file_change = statbuf; + } + } +} + +/* + * get values for time.now metric instances + */ +static void +simple_timenow_refresh(void) +{ + time_t t = time(NULL); + struct tm *tptr; + + tptr = localtime(&t); + timeslices[0].tm_field = tptr->tm_sec; + timeslices[1].tm_field = tptr->tm_min; + timeslices[2].tm_field = tptr->tm_hour; +} + +/* + * clear the time.now metric instance domain + */ +static void +simple_timenow_clear(void) +{ + int sts; + + sts = pmdaCacheOp(*now_indom, PMDA_CACHE_INACTIVE); + if (sts < 0) + __pmNotifyErr(LOG_ERR, "pmdaCacheOp(INACTIVE) failed: indom=%s: %s", + pmInDomStr(*now_indom), pmErrStr(sts)); +#ifdef DESPERATE + __pmdaCacheDump(stderr, *now_indom, 1); +#endif +} + +/* + * parse the configuration file for the time.now metric instance domain + */ +static void +simple_timenow_init(void) +{ + int i; + int sts; + int sep = __pmPathSeparator(); + FILE *fp; + char *p, *q; + char buf[SIMPLE_BUFSIZE]; + + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "simple.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if ((fp = fopen(mypath, "r")) == NULL) { + __pmNotifyErr(LOG_ERR, "fopen on %s failed: %s\n", + mypath, pmErrStr(-oserror())); + return; + } + if ((p = fgets(&buf[0], SIMPLE_BUFSIZE, fp)) == NULL) { + __pmNotifyErr(LOG_ERR, "fgets on %s found no data\n", mypath); + fclose(fp); + return; + } + if ((q = strchr(p, '\n')) != NULL) + *q = '\0'; /* remove eol character */ + + q = strtok(p, ","); /* and refresh using the updated file */ + while (q != NULL) { + for (i = 0; i < num_timeslices; i++) { + if (strcmp(timeslices[i].tm_name, q) == 0) { + sts = pmdaCacheStore(*now_indom, PMDA_CACHE_ADD, q, ×lices[i]); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "pmdaCacheStore failed: %s", pmErrStr(sts)); + fclose(fp); + return; + } + break; + } + } + if (i == num_timeslices) + __pmNotifyErr(LOG_WARNING, "ignoring \"%s\" in %s", q, mypath); + q = strtok(NULL, ","); + } +#ifdef DESPERATE + __pmdaCacheDump(stderr, *now_indom, 1); +#endif + if (pmdaCacheOp(*now_indom, PMDA_CACHE_SIZE_ACTIVE) < 1) + __pmNotifyErr(LOG_WARNING, "\"timenow\" instance domain is empty"); + + fclose(fp); +} + +/* + * support the storage of a value into the number of fetches count + */ +static int +simple_store(pmResult *result, pmdaExt *pmda) +{ + int i; + int j; + int val; + int sts = 0; + pmValueSet *vsp = NULL; + __pmID_int *pmidp = NULL; + + /* a store request may affect multiple metrics at once */ + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster == 0) { /* all storable metrics are cluster 0 */ + + switch (pmidp->item) { + case 0: /* simple.numfetch */ + val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 0; + } + numfetch = val; + break; + + case 1: /* simple.color */ + /* a store request may affect multiple instances at once */ + for (j = 0; j < vsp->numval && sts == 0; j++) { + + val = vsp->vlist[j].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 0; + } + if (val > 255) { + sts = PM_ERR_CONV; + val = 255; + } + + switch (vsp->vlist[j].inst) { + case 0: /* red */ + red = val; + break; + case 1: /* green */ + green = val; + break; + case 2: /* blue */ + blue = val; + break; + default: + sts = PM_ERR_INST; + } + } + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else if ((pmidp->cluster == 1 && + (pmidp->item == 2 || pmidp->item == 3)) || + (pmidp->cluster == 2 && pmidp->item == 4)) { + sts = PM_ERR_PERMISSION; + break; + } + else { + sts = PM_ERR_PMID; + break; + } + } + return sts; +} + + +/* + * Initialise the agent (both daemon and DSO). + */ +void +simple_init(pmdaInterface *dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_2, "simple DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.any.fetch = simple_fetch; + dp->version.any.store = simple_store; + dp->version.any.instance = simple_instance; + + pmdaSetFetchCallBack(dp, simple_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); +} + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, SIMPLE, + "simple.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + pmdaConnect(&dispatch); + simple_init(&dispatch); + simple_timenow_check(); + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/simple/simple.conf b/src/pmdas/simple/simple.conf new file mode 100644 index 0000000..a1927dc --- /dev/null +++ b/src/pmdas/simple/simple.conf @@ -0,0 +1 @@ +sec,min,hour diff --git a/src/pmdas/snmp/GNUmakefile b/src/pmdas/snmp/GNUmakefile new file mode 100644 index 0000000..8c50814 --- /dev/null +++ b/src/pmdas/snmp/GNUmakefile @@ -0,0 +1,53 @@ +#!gmake +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = snmp +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl snmp.conf +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + $(INSTALL) -m 644 snmp.conf $(PMDADIR)/snmp.conf + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/snmp/Install b/src/pmdas/snmp/Install new file mode 100755 index 0000000..999f0a6 --- /dev/null +++ b/src/pmdas/snmp/Install @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Copyright (c) 2011 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the SNMP gateway PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=snmp +perl_opt=true +daemon_opt=false +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/snmp/README b/src/pmdas/snmp/README new file mode 100644 index 0000000..15fe1d3 --- /dev/null +++ b/src/pmdas/snmp/README @@ -0,0 +1,52 @@ +The PCP SNMP gateway PMDA is a plugin that allows PCP clients to query +for data from SNMP agents. + +Depends on perl modules: +PCP::PMDA libpcp-pmda-perl +Net::SNMP libnet-snmp-perl + +The PCP names that are exported are (currently) + + snmp.host.$hostname.$oid + snmp.host.$hostname.$oid[$rownr] + +In the future I hope to propose an extension to the PCP agent protocol +to allow the use of virtual hostnames, which would change the names +exported. + +I also hope in the future to load in full MIB names, which would also +change the exported names to make them more readable. + +Also as a future target, as OIDs are queried for by the PCP client, +this gateway will dynamically create mappings between the PCP internal +ID and the SNMP OID. This creates limitations on the total number of +mappings available. + +SNMP uses an open ended hierachical namespace with a potentially unlimited +number of values. MIB files on the SNMP client are used to map between +dotted names and dotted numbers. These MIB files also provide type +information and help text for each OID + +PCP uses a 64bit value to uniquely identify each available value. This is +divided into: + 9 bits - "domain" number (./src/pmns/Make.stdpmid gives 56-58 to SNMP) + 12 bits - "cluster" number + 10 bits - "item" number (cluster+item identify the metric) + 32 bits - "instance" of the metric +PCP uses an open ended dotted text name to translate to this fixed 64bit value. +The PCP agent sends the text to number mapping to the PCP client as needed. + +This gateway is configured with a config file: +* define each SNMP hostname and credentials +* define a list of MIB namespaces to load (future) +* define a list of static OID -> PCP mappings (including room for tables..) +* a static mapping of a tree (and the max static size of that tree?) (future) +* a static "table" mapping for special use of the "instance" value +* define a "high water mark" for static mappings (future) + +If a query comes in for a static mapping, then the PCP value from the +config is used every time. If a query for a new mapping is received +then the gateway dynamically allocates the next free ID (starting from +MAXINT working downwards). This dynamic mapping might change on gateway +restarts or on idle timeouts. + diff --git a/src/pmdas/snmp/Remove b/src/pmdas/snmp/Remove new file mode 100755 index 0000000..7a412d9 --- /dev/null +++ b/src/pmdas/snmp/Remove @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Remove the SNMP PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=snmp + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/snmp/pmdasnmp.pl b/src/pmdas/snmp/pmdasnmp.pl new file mode 100755 index 0000000..058659d --- /dev/null +++ b/src/pmdas/snmp/pmdasnmp.pl @@ -0,0 +1,413 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2011-2012 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use FileHandle; +use PCP::PMDA; +use Net::SNMP qw(:asn1); + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; +$Data::Dumper::Useqq = 1; # PMDA log doesnt like binary :-( + +our $VERSION='0.3'; +my $db = {}; +my $option = { + max_row => 100, # default maximum number of rows for a table + pmid_per_host => 100, # default number of pmid's for each host +}; + +# SNMP string type name to numeric type number +# +my $snmptype2val = { + INTEGER => INTEGER32, + INTEGER32 => INTEGER32, + OCTET_STRING => OCTET_STRING, + STRING => OCTET_STRING, + OBJECT_IDENTIFIER => OBJECT_IDENTIFIER, + IPADDRESS => IPADDRESS, + COUNTER => COUNTER32, + COUNTER32 => COUNTER32, + GAUGE => GAUGE32, + GAUGE32 => GAUGE32, + UNSIGNED32 => UNSIGNED32, + TIMETICKS => TIMETICKS, + OPAQUE => OPAQUE, + COUNTER64 => COUNTER64, +}; + +# SNMP numeric type number to PCP type number +# +my $snmptype2pcp = { + 0x02 => { type=> PM_TYPE_32, sem=> PM_SEM_INSTANT }, # INTEGER32 + 0x04 => { type=> PM_TYPE_STRING, sem=> PM_SEM_DISCRETE }, # OCTET_STRING + 0x06 => { type=> PM_TYPE_STRING, sem=> PM_SEM_DISCRETE }, # OBJECT_IDENTIFIER + 0x40 => { type=> PM_TYPE_STRING, sem=> PM_SEM_DISCRETE }, # IPADDRESS + 0x41 => { type=> PM_TYPE_U32, sem=> PM_SEM_COUNTER }, # COUNTER32 + 0x42 => { type=> PM_TYPE_32, sem=> PM_SEM_INSTANT }, # GAUGE32 + 0x42 => { type=> PM_TYPE_U32, sem=> PM_SEM_INSTANT }, # UNSIGNED32 + 0x43 => { type=> PM_TYPE_64, sem=> PM_SEM_COUNTER }, # TIMETICKS + 0x44 => { type=> PM_TYPE_STRING, sem=> PM_SEM_DISCRETE }, # OPAQUE + 0x46 => { type=> PM_TYPE_64, sem=> PM_SEM_COUNTER }, # COUNTER64 +}; + +my $dom_rows = 0; # this indom nr used for generic row numbers + +my $pmda = PCP::PMDA->new('snmp', 56); + +# Read in the config file(s) +# +sub load_config { + my $db = shift; + + if (!defined $db->{hosts}) { + $db->{hosts} = {}; + } + if (!defined $db->{map}) { + $db->{map} = {}; + $db->{map}{hosts} = []; + $db->{map}{oids} = []; + } + + for my $filename (@_) { + my $fh = FileHandle->new($filename); + if (!defined $fh) { + warn "opening $filename $!"; + next; + } + + while(<$fh>) { + chomp; s/\r//g; + + # strip whitespace at the beginning and end of the line + s/^\s+//; + s/\s+$//; + + # strip comments + s/^#.*//; # line starts with a comment char + s/[^\\]#.*//; # non quoted comment char + + if (m/^$/) { + # empty lines, or lines that were all comment + next; + } + + if (m/^set\s+(\w+)\s+(.*)$/) { + # set an option + my $key = $1; + my $val = $2; + + $option->{$key}=$val; + } elsif (m/^host\s+(\S+)\s+(.*)$/) { + my $e = {}; + $e->{hostname}=$1; + $e->{community}=$2; + + # The reversed dotted hostname is used in the metric name + $e->{revname} = join('.',reverse(split('\.',$1))); + + # TODO - lazy create snmp sessions on first use + my ($session,$error) = Net::SNMP->session( + -hostname =>$e->{hostname}, + -community=>$e->{community}, + ); + if (!$session) { + warn("SNMP session to $e->{hostname}: $error"); + $e->{error}=$error; + } else { + $e->{snmp}=$session; + $e->{snmp}->translate([-timeticks=>0]); + } + $db->{hosts}{$1} = $e; + my $id = scalar @{$db->{map}{hosts}}; + # TODO - allow this pmid 'index base' to be set + $e->{id} = $id; + @{$db->{map}{hosts}}[$id]=$e; + } elsif (m/^map\s+(single|column)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) { + my $snmptype = $snmptype2val->{$3}; + if (!defined $snmptype) { + warn("Invalid SNMP type '$3' on oid '$2'\n"); + next; + } + my $e = {}; + my $id = $4; + if ($id eq '+') { + # select the next available number + $id = scalar @{$db->{map}{oids}}; + } + if ($id > $option->{pmid_per_host}) { + warn("More metrics than allowed by pmid_per_host"); + next; + } + $e->{type}=$1; + $e->{oid}=$2; + $e->{snmptype}=$snmptype; + $e->{id}=$id; + $e->{text}=$5; + @{$db->{map}{oids}}[$id]=$e; + } else { + warn("Unrecognised config line: $_\n"); + } + # TODO - add map tree, mib load + } + } + + $db->{max}{hosts} = scalar keys %{$db->{hosts}}; + $db->{max}{oids} = scalar @{$db->{map}{oids}}; + $db->{max}{static} = $db->{max}{hosts} * $option->{pmid_per_host}; + # any PMID above max static is available for dynamicly created mappings + + return $db; +} + +# Create the fake generic rows indom +# TODO - demand create the rows indoms +sub db_create_indom { + my ($db) = @_; + + my @dom; + for my $row (0..$option->{max_row}) { + # first is id, second is string description + # for now, both are the same + # TODO - populate the indom with rational names from an SNMP column + push @dom,$row,$row; + } + $pmda->add_indom($dom_rows,\@dom,'SNMP rows',''); +} + +# Using the mappings, define all the metrics +# +sub db_add_metrics { + my ($db) = @_; + + # TODO - nuke the PMDA.xs current list of metrics here + # (there is a clear_metrics() in the xs that might be adapted to work) + + # add our version + $pmda->add_metric(pmda_pmid(0,0), PM_TYPE_STRING, + PM_INDOM_NULL, PM_SEM_DISCRETE, + pmda_units(0,0,0,0,0,0), "snmp.version", '', ''); + + for my $host (@{$db->{map}{hosts}}) { + # calculate the pmid for the first metric for this host + my $hostbase = $host->{id} * $option->{pmid_per_host}; + + # skip hosts that did not setup their snmp session + next if (!$host->{snmp}); + + for my $e (@{$db->{map}{oids}}) { + # for each predefined static mapping, register a metric + + if (!defined $e) { + next; + } + my $id = $hostbase + $e->{id}; + + # hack around the too transparent opaque datatype + my $cluster = int($id /1024); + my $item = $id %1024; + + my $type = $snmptype2pcp->{$e->{snmptype}}; + if (!defined $type) { + warn("Unknown type=$type for id=$e->{id}\n"); + next; + } + + my $indom; + if ($e->{type} eq 'single') { + $indom = PM_INDOM_NULL; + } elsif ($e->{type} eq 'column') { + # TODO - use metric specific indom, for now, just use generic + $indom = $dom_rows; + $e->{indom} = $indom; + } else { + warn("Unknown map type = $e->{type}\n"); + next; + } + $pmda->add_metric(pmda_pmid($cluster,$item), + $type->{type}, + $indom, $type->{sem}, + pmda_units(0,0,0,0,0,0), + 'snmp.host.'.$host->{revname}.'.'.$e->{oid}, $e->{text}, '' + ); + } + } +} + +# debug when fetch is called +# fetch_func is called with no params during a "fetch", after refreshing the +# PMNS before calling refresh_func +# +sub fetch { + if ($option->{debug}) { + $pmda->log("fetch"); + } +} + +# debug when instance is called +# instance_func is called with "indom" param during a "instance", after +# refreshing the PMNS before calling pmdaInstance +# +sub instance { + my ($indom) = @_; + if ($option->{debug}) { + $pmda->log("instance $indom"); + } +} + +sub fetch_callback +{ + my ($cluster, $item, $inst) = @_; + my $id = $cluster*1024 + $item; + + if ($option->{debug}) { + my $metric_name = pmda_pmid_name($cluster, $item); + $pmda->log("fetch_callback $metric_name $cluster:$item ($inst)"); + } + + if ($id == 0) { + return ($VERSION,1); + } + + my $hostnr = int($id / $option->{pmid_per_host}); + my $host = @{$db->{map}{hosts}}[$hostnr]; + if (!defined $host) { + return (PM_ERR_NOTHOST, 0); + } + + my $map = @{$db->{map}{oids}}[$id % $option->{pmid_per_host}]; + if (!defined $map) { + return (PM_ERR_PMID, 0); + } + my $oid = $map->{oid}; + + if (defined $map->{indom}) { + # only metrics with rows have an indom + $oid.='.'.$inst; + } + + # TODO - maybe check if a map single has been called with an inst other + # than PM_INDOM_NULL + + if ($option->{debug}) { + $pmda->log("fetch_callback hostnr=$hostnr rownr=$inst"); + } + + if (!defined $host->{snmp}) { + # We have no snmp object for this host + # FIXME - a better errno? + return (PM_ERR_EOF, 0); + } + my $snmp = $host->{snmp}; + + my $result = $snmp->get_request( + -varbindlist=>[ + $oid, + ] + ); + + if (!$result) { + # We didnt get a valid snmp response + return (PM_ERR_PMID, 0); + } + + my $types = $snmp->var_bind_types(); + if ($map->{snmptype} != $types->{$oid}) { + return (PM_ERR_CONV, 0); + } + return ($result->{$oid},1); +} + +load_config($db, + pmda_config('PCP_PMDAS_DIR').'/snmp/snmp.conf', +# 'snmp.conf' +); + +db_create_indom($db); + +db_add_metrics($db); + +$pmda->set_fetch(\&fetch); +$pmda->set_instance(\&instance); +$pmda->set_fetch_callback(\&fetch_callback); + +if ($option->{debug}) { + $pmda->log("db=".Dumper($db)."\n"); + $pmda->log("option=".Dumper($option)."\n"); +} +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdasnmp - Gateway from SNMP to PCP (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +provides a generic gateway from PCP queries from a PCP client to SNMP queries +to one or more SNMP agents. + +=head1 INSTALLATION + +If you want access to the SNMP gateway performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/snmp + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/snmp + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 CONFIGURATION + +TODO: define config file format here - map/set/host/... etc + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/snmp/snmp.conf + +optional configuration file for B + +=item $PCP_PMDAS_DIR/snmp/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/snmp/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/snmp.log + +default log file for error and warn() messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and SNMP diff --git a/src/pmdas/snmp/snmp.conf b/src/pmdas/snmp/snmp.conf new file mode 100644 index 0000000..ed7b88b --- /dev/null +++ b/src/pmdas/snmp/snmp.conf @@ -0,0 +1,44 @@ +# Simple, first pass at a config file. I expect to need a more expressive +# file format in the future + +# hashes begin comments +# comments can begin in the # middle of a line + +# define a host +#host $hostname $community +# TODO - allow the pmid 'index base' to be set for each host +# currently only community based auth is supported +host localhost public + +# load a mib (future) - gives name to number oid mappings +#mib $mibfilename + +# static mapping +# single maps a single oid +# column maps a simple table column where the last octet in the oid is the row +# id's start at 1 and redefininitions result in the last define winning +# the snmptype is used to calculate the pcp metric type to use +# the text is used as the metric short help text +#map (single|column) $oid $snmptype $id $text + +map single 1.3.6.1.2.1.1.3.0 TIMETICKS 1 sysUpTime +map single 1.3.6.1.2.1.1.5.0 STRING 2 sysName + +map column 1.3.6.1.2.1.2.2.1.2 STRING 10 ifDescr +map column 1.3.6.1.2.1.2.2.1.5 GAUGE32 + ifSpeed +map column 1.3.6.1.2.1.2.2.1.10 COUNTER32 + ifInOctets + +# TODO - work out some kind of static walk define +#map tree $oid $id_start $id_max $text_prefix + +# For the moment, table oids are limited to this max number +set max_row 50 + +# Set the maximum number of metrics per host. This is used to create the +# metric ID and thus if changed will affect merging data with older archives. +set pmid_per_host 1000 + +# set an option +#set key val +set debug 1 + diff --git a/src/pmdas/solaris/GNUmakefile b/src/pmdas/solaris/GNUmakefile new file mode 100644 index 0000000..e5f858c --- /dev/null +++ b/src/pmdas/solaris/GNUmakefile @@ -0,0 +1,82 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = solaris +DOMAIN = SOLARIS +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +PMDAINIT = solaris_init +CMDTARGET = pmdasolaris +LIBTARGET = pmda_solaris.so +CONF_LINE = "solaris 75 dso $(PMDAINIT) $(PMDADIR)/$(LIBTARGET)" + +CFILES = solaris.c data.c sysinfo.c disk.c zpool.c zfs.c \ + zpool_perdisk.c netmib2.c netlink.c kvm.c arcstats.c vnops.c + +BARE_NS = disk kernel mem network hinv zpool zfs zpool_perdisk +PMNS = $(BARE_NS:%=pmns.%) + +LSRCFILES = $(PMNS) help root common.h clusters.h netmib2.h +HELPTARGETS = help.dir help.pag +VERSION_SCRIPT = exports + +LDIRT = domain.h *.log $(HELPTARGETS) root_solaris + +LLDLIBS = $(PCP_PMDALIB) -lkstat -lzfs -lnvpair -lkvm -ldevinfo + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "solaris" +build-me: root_solaris domain.h $(LIBTARGET) $(CMDTARGET) $(HELPTARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h $(HELPTARGETS) $(PMDADIR) + $(INSTALL) -m 755 $(LIBTARGET) $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root_solaris $(PCP_VAR_DIR)/pmns/root_solaris +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +$(OBJECTS): common.h + +$(HELPTARGETS): help root_solaris + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_solaris -v 2 -o help < help + +root_solaris: ../../pmns/stdpmid $(PMNS) root + rm -f root_solaris + sed -e 's;;"../../pmns/stdpmid";' root_solaris + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +$(LIBTARGET): $(VERSION_SCRIPT) diff --git a/src/pmdas/solaris/arcstats.c b/src/pmdas/solaris/arcstats.c new file mode 100644 index 0000000..75cd17c --- /dev/null +++ b/src/pmdas/solaris/arcstats.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Extract information about ZFS' Adjustable Replacement Cache + * + * The stats are in the sections called "arc_stats" of module zfs + */ + +#include +#include "common.h" + +static kstat_t *arcstats; +int arcstats_fresh; + +void +arcstats_refresh(void) +{ + kstat_ctl_t *kc; + arcstats_fresh = 0; + if ((kc = kstat_ctl_update()) == NULL) + return; + if ((arcstats = kstat_lookup(kc, "zfs", -1, "arcstats")) != NULL) + arcstats_fresh = kstat_read(kc, arcstats, NULL) != -1; +} + +int +arcstats_fetch(pmdaMetric *pm, int inst, pmAtomValue *av) +{ + metricdesc_t *md = pm->m_user; + char *metric = (char *)md->md_offset; + kstat_named_t *kn; + + if (!arcstats_fresh) + return 0; + + if ((kn = kstat_data_lookup(arcstats, metric)) != NULL) + return kstat_named_to_pmAtom(kn, av); + + return 0; +} diff --git a/src/pmdas/solaris/clusters.h b/src/pmdas/solaris/clusters.h new file mode 100644 index 0000000..9e44ccb --- /dev/null +++ b/src/pmdas/solaris/clusters.h @@ -0,0 +1,20 @@ +#ifndef __PMDA_SOLARIS_CLUSTERS_H +#define __PMDA_SOLARIS_CLUSTERS_H + +/* + * PMID cluster numbers + * + * Clusters are used to index method[] table and shall be contigious + */ +#define SCLR_SYSINFO 0 +#define SCLR_DISK 1 +#define SCLR_NETIF 2 +#define SCLR_ZPOOL 3 +#define SCLR_ZFS 4 +#define SCLR_ZPOOL_PERDISK 5 +#define SCLR_NETLINK 6 +#define SCLR_FSFLUSH 7 +#define SCLR_ARCSTATS 8 +#define SCLR_FILESYS 9 + +#endif diff --git a/src/pmdas/solaris/common.h b/src/pmdas/solaris/common.h new file mode 100644 index 0000000..469725e --- /dev/null +++ b/src/pmdas/solaris/common.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __PMDASOLARIS_COMMON_H +#define __PMDASOLARIS_COMMON_H + +#include +#include +#include + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "clusters.h" + +#include +#include + +typedef struct { + const char *m_name; + void (*m_init)(int); + void (*m_prefetch)(void); + int (*m_fetch)(pmdaMetric *, int, pmAtomValue *); + int m_fetched; + uint64_t m_elapsed; + uint64_t m_hits; +} method_t; + +extern method_t methodtab[]; +extern const int methodtab_sz; + +extern void init_data(int); + +extern void sysinfo_init(int); +extern void sysinfo_prefetch(void); +extern int sysinfo_fetch(pmdaMetric *, int, pmAtomValue *); + +extern void disk_init(int); +extern void disk_prefetch(void); +extern int disk_fetch(pmdaMetric *, int, pmAtomValue *); + +void zpool_init(int); +void zpool_refresh(void); +int zpool_fetch(pmdaMetric *, int, pmAtomValue *); + +void zfs_init(int); +void zfs_refresh(void); +int zfs_fetch(pmdaMetric *, int, pmAtomValue *); + +void zpool_perdisk_init(int); +void zpool_perdisk_refresh(void); +int zpool_perdisk_fetch(pmdaMetric *, int, pmAtomValue *); + +void netlink_init(int); +void netlink_refresh(void); +int netlink_fetch(pmdaMetric *, int, pmAtomValue *); + +void kvm_init(int); +void kvm_refresh(void); +int kvm_fetch(pmdaMetric *, int, pmAtomValue *); + +void arcstats_refresh(void); +int arcstats_fetch(pmdaMetric *, int, pmAtomValue *); + +void vnops_init(int); +void vnops_refresh(void); +int vnops_fetch(pmdaMetric *, int, pmAtomValue *); + +/* + * metric descriptions + */ +typedef struct { + const char *md_name; + pmDesc md_desc; // PMDA's idea of the semantics + ptrdiff_t md_offset; // offset into kstat stats structure + uint64_t md_elapsed; + uint64_t md_hits; +} metricdesc_t; + +extern metricdesc_t metricdesc[]; +extern pmdaMetric *metrictab; +extern int metrictab_sz; + +#define DISK_INDOM 0 +#define CPU_INDOM 1 +#define NETIF_INDOM 2 +#define ZPOOL_INDOM 3 +#define ZFS_INDOM 4 +#define ZPOOL_PERDISK_INDOM 5 +#define NETLINK_INDOM 6 +#define ZFS_SNAP_INDOM 7 +#define LOADAVG_INDOM 8 +#define PREFETCH_INDOM 9 +#define METRIC_INDOM 10 +#define FILESYS_INDOM 11 +#define FSTYPE_INDOM 12 + +extern pmdaIndom indomtab[]; +extern int indomtab_sz; + +/* + * kstat() control + */ +kstat_ctl_t *kstat_ctl_update(void); +void kstat_ctl_needs_update(void); +int kstat_named_to_pmAtom(const kstat_named_t *, pmAtomValue *); +int kstat_named_to_typed_atom(const kstat_named_t *, int, pmAtomValue *); + +/* Snarfed from usr/src/uts/common/fs/fsflush.c in OpenSolaris source tree */ +typedef struct { + ulong_t fsf_scan; /* number of pages scanned */ + ulong_t fsf_examined; /* number of page_t's actually examined, can */ + /* be less than fsf_scan due to large pages */ + ulong_t fsf_locked; /* pages we actually page_lock()ed */ + ulong_t fsf_modified; /* number of modified pages found */ + ulong_t fsf_coalesce; /* number of page coalesces done */ + ulong_t fsf_time; /* nanoseconds of run time */ + ulong_t fsf_releases; /* number of page_release() done */ +} fsf_stat_t; + +#endif diff --git a/src/pmdas/solaris/data.c b/src/pmdas/solaris/data.c new file mode 100644 index 0000000..6d09a0e --- /dev/null +++ b/src/pmdas/solaris/data.c @@ -0,0 +1,1462 @@ +/* + * Data structures that define metrics and control the Solaris PMDA + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2010 Max Matveev. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" +#include "netmib2.h" +#include +#include + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +method_t methodtab[] = { + { "sysinfo", sysinfo_init, sysinfo_prefetch, sysinfo_fetch }, + { "disk", disk_init, disk_prefetch, disk_fetch }, + { "netmib2", netmib2_init, netmib2_refresh, netmib2_fetch }, + { "zpool", zpool_init, zpool_refresh, zpool_fetch }, + { "zfs", zfs_init, zfs_refresh, zfs_fetch }, + { "zpool_vdev", zpool_perdisk_init, zpool_perdisk_refresh, zpool_perdisk_fetch }, + { "netlink", netlink_init, netlink_refresh, netlink_fetch }, + { "kvm", kvm_init, kvm_refresh, kvm_fetch }, + { "zfs_arc", NULL, arcstats_refresh, arcstats_fetch }, + { "filesystem", vnops_init, vnops_refresh, vnops_fetch } +}; + +const int methodtab_sz = ARRAY_SIZE(methodtab); +static pmdaInstid prefetch_insts[ARRAY_SIZE(methodtab)]; + +static pmdaInstid loadavg_insts[] = { + {1, "1 minute"}, + {5, "5 minute"}, + {15, "15 minute"} +}; + +pmdaMetric *metrictab; + +#define SYSINFO_OFF(field) ((ptrdiff_t)&((cpu_stat_t *)0)->cpu_sysinfo.field) +#define KSTAT_IO_OFF(field) ((ptrdiff_t)&((kstat_io_t *)0)->field) +#define VDEV_OFFSET(field) ((ptrdiff_t)&((vdev_stat_t *)0)->field) +#define NM2_UDP_OFFSET(field) ((ptrdiff_t)&(nm2_udp.field)) +#define NM2_NETIF_OFFSET(field) ((ptrdiff_t)&((nm2_netif_stats_t *)0)->field) +#define FSF_STAT_OFFSET(field) ((ptrdiff_t)&((fsf_stat_t *)0)->field) + +/* + * all metrics supported in this PMDA - one table entry for each metric + */ +metricdesc_t metricdesc[] = { + + { "kernel.all.cpu.idle", + { PMDA_PMID(SCLR_SYSINFO,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_IDLE]) }, + + { "kernel.all.cpu.user", + { PMDA_PMID(SCLR_SYSINFO,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_USER]) }, + + { "kernel.all.cpu.sys", + { PMDA_PMID(SCLR_SYSINFO,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_KERNEL]) }, + + { "kernel.all.cpu.wait.total", + { PMDA_PMID(SCLR_SYSINFO,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_WAIT]) }, + + { "kernel.percpu.cpu.idle", + { PMDA_PMID(SCLR_SYSINFO,4), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_IDLE]) }, + + { "kernel.percpu.cpu.user", + { PMDA_PMID(SCLR_SYSINFO,5), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_USER]) }, + + { "kernel.percpu.cpu.sys", + { PMDA_PMID(SCLR_SYSINFO,6), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_KERNEL]) }, + + { "kernel.percpu.cpu.wait.total", + { PMDA_PMID(SCLR_SYSINFO,7), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(cpu[CPU_WAIT]) }, + + { "kernel.all.cpu.wait.io", + { PMDA_PMID(SCLR_SYSINFO,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(wait[W_IO]) }, + + { "kernel.all.cpu.wait.pio", + { PMDA_PMID(SCLR_SYSINFO,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(wait[W_PIO]) }, + + { "kernel.all.cpu.wait.swap", + { PMDA_PMID(SCLR_SYSINFO,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(wait[W_SWAP]) }, + + { "kernel.percpu.cpu.wait.io", + { PMDA_PMID(SCLR_SYSINFO,11), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(wait[W_IO]) }, + + { "kernel.percpu.cpu.wait.pio", + { PMDA_PMID(SCLR_SYSINFO,12), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(wait[W_PIO]) }, + + { "kernel.percpu.cpu.wait.swap", + { PMDA_PMID(SCLR_SYSINFO,13), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, SYSINFO_OFF(wait[W_SWAP]) }, + + { "kernel.all.io.bread", + { PMDA_PMID(SCLR_SYSINFO,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(bread) }, + + { "kernel.all.io.bwrite", + { PMDA_PMID(SCLR_SYSINFO,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(bwrite) }, + + { "kernel.all.io.lread", + { PMDA_PMID(SCLR_SYSINFO,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(lread) }, + + { "kernel.all.io.lwrite", + { PMDA_PMID(SCLR_SYSINFO,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(lwrite) }, + + { "kernel.percpu.io.bread", + { PMDA_PMID(SCLR_SYSINFO,18), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(bread) }, + + { "kernel.percpu.io.bwrite", + { PMDA_PMID(SCLR_SYSINFO,19), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(bwrite) }, + + { "kernel.percpu.io.lread", + { PMDA_PMID(SCLR_SYSINFO,20), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(lread) }, + + { "kernel.percpu.io.lwrite", + { PMDA_PMID(SCLR_SYSINFO,21), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(lwrite) }, + + { "kernel.all.syscall", + { PMDA_PMID(SCLR_SYSINFO,22), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(syscall) }, + + { "kernel.all.pswitch", + { PMDA_PMID(SCLR_SYSINFO,23), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(pswitch) }, + + { "kernel.percpu.syscall", + { PMDA_PMID(SCLR_SYSINFO,24), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(syscall) }, + + { "kernel.percpu.pswitch", + { PMDA_PMID(SCLR_SYSINFO,25), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(pswitch) }, + + { "kernel.all.io.phread", + { PMDA_PMID(SCLR_SYSINFO,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(phread) }, + + { "kernel.all.io.phwrite", + { PMDA_PMID(SCLR_SYSINFO,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(phwrite) }, + + { "kernel.all.io.intr", + { PMDA_PMID(SCLR_SYSINFO,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(intr) }, + + { "kernel.percpu.io.phread", + { PMDA_PMID(SCLR_SYSINFO,29), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(phread) }, + + { "kernel.percpu.io.phwrite", + { PMDA_PMID(SCLR_SYSINFO,30), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(phwrite) }, + + { "kernel.percpu.io.intr", + { PMDA_PMID(SCLR_SYSINFO,31), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(intr) }, + + { "kernel.all.trap", + { PMDA_PMID(SCLR_SYSINFO,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(trap) }, + + { "kernel.all.sysexec", + { PMDA_PMID(SCLR_SYSINFO,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysexec) }, + + { "kernel.all.sysfork", + { PMDA_PMID(SCLR_SYSINFO,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysfork) }, + + { "kernel.all.sysvfork", + { PMDA_PMID(SCLR_SYSINFO,35), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysvfork) }, + + { "kernel.all.sysread", + { PMDA_PMID(SCLR_SYSINFO,36), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysread) }, + + { "kernel.all.syswrite", + { PMDA_PMID(SCLR_SYSINFO,37), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(syswrite) }, + + { "kernel.percpu.trap", + { PMDA_PMID(SCLR_SYSINFO,38), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(trap) }, + + { "kernel.percpu.sysexec", + { PMDA_PMID(SCLR_SYSINFO,39), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysexec) }, + + { "kernel.percpu.sysfork", + { PMDA_PMID(SCLR_SYSINFO,40), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysfork) }, + + { "kernel.percpu.sysvfork", + { PMDA_PMID(SCLR_SYSINFO,41), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysvfork) }, + + { "kernel.percpu.sysread", + { PMDA_PMID(SCLR_SYSINFO,42), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(sysread) }, + + { "kernel.percpu.syswrite", + { PMDA_PMID(SCLR_SYSINFO,43), PM_TYPE_U32, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, SYSINFO_OFF(syswrite) }, + + { "disk.all.read", + { PMDA_PMID(SCLR_DISK,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, KSTAT_IO_OFF(reads) }, + + { "disk.all.write", + { PMDA_PMID(SCLR_DISK,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, KSTAT_IO_OFF(writes) }, + + { "disk.all.total", + { PMDA_PMID(SCLR_DISK,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, -1}, + + { "disk.all.read_bytes", + { PMDA_PMID(SCLR_DISK,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, KSTAT_IO_OFF(nread) }, + + { "disk.all.write_bytes", + { PMDA_PMID(SCLR_DISK,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, KSTAT_IO_OFF(nwritten) }, + + { "disk.all.total_bytes", + { PMDA_PMID(SCLR_DISK,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, -1}, + + { "disk.dev.read", + { PMDA_PMID(SCLR_DISK,10), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, KSTAT_IO_OFF(reads) }, + + + { "disk.dev.write", + { PMDA_PMID(SCLR_DISK,11), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, KSTAT_IO_OFF(writes) }, + + { "disk.dev.total", + { PMDA_PMID(SCLR_DISK,12), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, -1}, + + { "disk.dev.read_bytes", + { PMDA_PMID(SCLR_DISK,13), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, KSTAT_IO_OFF(nread) }, + + { "disk.dev.write_bytes", + { PMDA_PMID(SCLR_DISK,14), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, KSTAT_IO_OFF(nwritten) }, + + { "disk.dev.total_bytes", + { PMDA_PMID(SCLR_DISK,15), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, -1}, + + { "hinv.ncpu", + { PMDA_PMID(SCLR_SYSINFO,56), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1}, + + { "hinv.ndisk", + { PMDA_PMID(SCLR_DISK,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1}, + + { "hinv.nfilesys", + { PMDA_PMID(SCLR_FILESYS,1023), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1}, + + { "pmda.uname", + { PMDA_PMID(SCLR_SYSINFO,107), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1 }, + + { "hinv.pagesize", + { PMDA_PMID(SCLR_SYSINFO,108), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, -1 }, + + { "hinv.physmem", + { PMDA_PMID(SCLR_SYSINFO,109), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) + }, -1 }, + + { "zpool.capacity", + { PMDA_PMID(SCLR_ZPOOL,2), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, VDEV_OFFSET(vs_space) }, + { "zpool.used", + { PMDA_PMID(SCLR_ZPOOL,3), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, VDEV_OFFSET(vs_alloc) }, + { "zpool.checksum_errors", + { PMDA_PMID(SCLR_ZPOOL,4), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_checksum_errors) }, + { "zpool.self_healed", + { PMDA_PMID(SCLR_ZPOOL,5), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, VDEV_OFFSET(vs_self_healed) }, + { "zpool.in.bytes", + { PMDA_PMID(SCLR_ZPOOL,6), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, VDEV_OFFSET(vs_bytes[ZIO_TYPE_READ]) }, + { "zpool.in.ops", + { PMDA_PMID(SCLR_ZPOOL,7), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_ops[ZIO_TYPE_READ]) }, + { "zpool.in.errors", + { PMDA_PMID(SCLR_ZPOOL,8), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_read_errors) }, + { "zpool.out.bytes", + { PMDA_PMID(SCLR_ZPOOL,9), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, VDEV_OFFSET(vs_bytes[ZIO_TYPE_WRITE]) }, + { "zpool.out.ops", + { PMDA_PMID(SCLR_ZPOOL,10), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_ops[ZIO_TYPE_WRITE]) }, + { "zpool.out.errors", + { PMDA_PMID(SCLR_ZPOOL,11), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_write_errors) }, + { "zpool.ops.noops", + { PMDA_PMID(SCLR_ZPOOL,12), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_ops[ZIO_TYPE_NULL]) }, + { "zpool.ops.ioctls", + { PMDA_PMID(SCLR_ZPOOL,13), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_ops[ZIO_TYPE_WRITE]) }, + { "zpool.ops.claims", + { PMDA_PMID(SCLR_ZPOOL,14), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_ops[ZIO_TYPE_WRITE]) }, + { "zpool.ops.frees", + { PMDA_PMID(SCLR_ZPOOL,15), PM_TYPE_U64, ZPOOL_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_ops[ZIO_TYPE_WRITE]) }, + { "zfs.used.total", + { PMDA_PMID(SCLR_ZFS,10), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_USED }, + { "zfs.available", + { PMDA_PMID(SCLR_ZFS,0), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_AVAILABLE }, + { "zfs.quota", + { PMDA_PMID(SCLR_ZFS,1), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_QUOTA }, + { "zfs.reservation", + { PMDA_PMID(SCLR_ZFS,2), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_RESERVATION }, + { "zfs.compression", + { PMDA_PMID(SCLR_ZFS,3), PM_TYPE_DOUBLE, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, ZFS_PROP_COMPRESSRATIO }, + { "zfs.copies", + { PMDA_PMID(SCLR_ZFS,4), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, ZFS_PROP_COPIES }, + { "zfs.used.byme", + { PMDA_PMID(SCLR_ZFS,11), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_USEDDS }, + { "zfs.used.bysnapshots", + { PMDA_PMID(SCLR_ZFS,12), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_USEDSNAP }, + { "zfs.used.bychildren", + { PMDA_PMID(SCLR_ZFS,13), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_USEDCHILD }, + + { "network.udp.ipackets", + { PMDA_PMID(SCLR_NETIF,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_UDP_OFFSET(ipackets) }, + { "network.udp.opackets", + { PMDA_PMID(SCLR_NETIF,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_UDP_OFFSET(opackets) }, + { "network.udp.ierrors", + { PMDA_PMID(SCLR_NETIF,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_UDP_OFFSET(ierrors) }, + { "network.udp.oerrors", + { PMDA_PMID(SCLR_NETIF,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_UDP_OFFSET(oerrors) }, + + { "network.interface.mtu", + { PMDA_PMID(SCLR_NETIF,0), PM_TYPE_U32, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, NM2_NETIF_OFFSET(mtu) }, + { "network.interface.in.packets", + { PMDA_PMID(SCLR_NETIF,2), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(ipackets) }, + { "network.interface.in.bytes", + { PMDA_PMID(SCLR_NETIF,3), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, NM2_NETIF_OFFSET(ibytes) }, + { "network.interface.in.bcasts", + { PMDA_PMID(SCLR_NETIF,4), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(ibcast) }, + { "network.interface.in.mcasts", + { PMDA_PMID(SCLR_NETIF,5), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(imcast) }, + { "network.interface.out.packets", + { PMDA_PMID(SCLR_NETIF,9), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(opackets) }, + { "network.interface.out.bytes", + { PMDA_PMID(SCLR_NETIF,10), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, NM2_NETIF_OFFSET(obytes) }, + { "network.interface.out.bcasts", + { PMDA_PMID(SCLR_NETIF,11), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(obcast) }, + { "network.interface.out.mcasts", + { PMDA_PMID(SCLR_NETIF,12), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(omcast) }, + { "network.interface.in.errors", + { PMDA_PMID(SCLR_NETIF,1), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(ierrors) }, + { "network.interface.out.errors", + { PMDA_PMID(SCLR_NETIF,8), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(oerrors) }, + { "network.interface.in.drops", + { PMDA_PMID(SCLR_NETIF,6), PM_TYPE_U32, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(idrops) }, + { "network.interface.out.drops", + { PMDA_PMID(SCLR_NETIF,13), PM_TYPE_U32, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(odrops) }, + { "network.interface.in.delivers", + { PMDA_PMID(SCLR_NETIF,7), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_NETIF_OFFSET(delivered) }, + { "network.udp.noports", + { PMDA_PMID(SCLR_NETIF,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_UDP_OFFSET(noports) }, + { "network.udp.overflows", + { PMDA_PMID(SCLR_NETIF,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, NM2_UDP_OFFSET(overflows) }, + + { "zpool.state", + { PMDA_PMID(SCLR_ZPOOL,0), PM_TYPE_STRING, ZPOOL_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, 0 }, + + { "zpool.state_int", + { PMDA_PMID(SCLR_ZPOOL,1), PM_TYPE_U32, ZPOOL_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, 0 }, + { "zpool.perdisk.state", + { PMDA_PMID(SCLR_ZPOOL_PERDISK,0), PM_TYPE_STRING, ZPOOL_PERDISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, VDEV_OFFSET(vs_state) }, + { "zpool.perdisk.state_int", + { PMDA_PMID(SCLR_ZPOOL_PERDISK,1), PM_TYPE_U32, ZPOOL_PERDISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, 0 }, + { "zpool.perdisk.checksum_errors", + { PMDA_PMID(SCLR_ZPOOL_PERDISK,2), PM_TYPE_U64, ZPOOL_PERDISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_checksum_errors) }, + { "zpool.perdisk.self_healed", + { PMDA_PMID(SCLR_ZPOOL_PERDISK,3), PM_TYPE_U64, ZPOOL_PERDISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, VDEV_OFFSET(vs_self_healed) }, + { "zpool.perdisk.in.errors", + { PMDA_PMID(SCLR_ZPOOL_PERDISK,4), PM_TYPE_U64, ZPOOL_PERDISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_read_errors) }, + { "zpool.perdisk.out.errors", + { PMDA_PMID(SCLR_ZPOOL_PERDISK,5), PM_TYPE_U64, ZPOOL_PERDISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, VDEV_OFFSET(vs_write_errors) }, + + { "network.link.in.errors", + { PMDA_PMID(SCLR_NETLINK,4), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ierrors" }, + { "network.link.in.packets", + { PMDA_PMID(SCLR_NETLINK,5), PM_TYPE_U64, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ipackets64" }, + { "network.link.in.bytes", + { PMDA_PMID(SCLR_NETLINK,6), PM_TYPE_U64, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"rbytes64" }, + { "network.link.in.bcasts", + { PMDA_PMID(SCLR_NETLINK,7), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"brdcstrcv" }, + { "network.link.in.mcasts", + { PMDA_PMID(SCLR_NETLINK,8), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"multircv" }, + { "network.link.in.nobufs", + { PMDA_PMID(SCLR_NETLINK,9), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"norcvbuf" }, + { "network.link.out.errors", + { PMDA_PMID(SCLR_NETLINK,10), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"oerrors" }, + { "network.link.out.packets", + { PMDA_PMID(SCLR_NETLINK,11), PM_TYPE_U64, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"opackets64" }, + { "network.link.out.bytes", + { PMDA_PMID(SCLR_NETLINK,12), PM_TYPE_U64, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"obytes64" }, + { "network.link.out.bcasts", + { PMDA_PMID(SCLR_NETLINK,13), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"brdcstxmt" }, + { "network.link.out.mcasts", + { PMDA_PMID(SCLR_NETLINK,14), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"multixmt" }, + { "network.link.out.nobufs", + { PMDA_PMID(SCLR_NETLINK,15), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"noxmtbuf" }, + { "network.link.collisions", + { PMDA_PMID(SCLR_NETLINK,0), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"collisions" }, + { "network.link.state", + { PMDA_PMID(SCLR_NETLINK,1), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"link_state" }, + { "network.link.duplex", + { PMDA_PMID(SCLR_NETLINK,2), PM_TYPE_U32, NETLINK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"link_duplex" }, + { "network.link.speed", + { PMDA_PMID(SCLR_NETLINK,3), PM_TYPE_U64, NETLINK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ifspeed" }, + + { "zfs.recordsize", + { PMDA_PMID(SCLR_ZFS,5), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_RECORDSIZE }, + { "zfs.refquota", + { PMDA_PMID(SCLR_ZFS,6), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_REFQUOTA }, + { "zfs.refreservation", + { PMDA_PMID(SCLR_ZFS,7), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_REFRESERVATION }, + { "zfs.used.byrefreservation", + { PMDA_PMID(SCLR_ZFS,14), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_USEDREFRESERV }, + { "zfs.referenced", + { PMDA_PMID(SCLR_ZFS,8), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_REFERENCED }, + { "zfs.nsnapshots", + { PMDA_PMID(SCLR_ZFS,9), PM_TYPE_U64, ZFS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, -1 }, + { "zfs.snapshot.used", + { PMDA_PMID(SCLR_ZFS,15), PM_TYPE_U64, ZFS_SNAP_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_USED }, + { "zfs.snapshot.referenced", + { PMDA_PMID(SCLR_ZFS,16), PM_TYPE_U64, ZFS_SNAP_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, ZFS_PROP_REFERENCED }, + { "zfs.snapshot.compression", + { PMDA_PMID(SCLR_ZFS,17), PM_TYPE_DOUBLE, ZFS_SNAP_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, ZFS_PROP_COMPRESSRATIO }, + { "kernel.all.load", + { PMDA_PMID(SCLR_SYSINFO,135), PM_TYPE_FLOAT, LOADAVG_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, 0 }, + + { "kernel.fsflush.scanned", + { PMDA_PMID(SCLR_FSFLUSH,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, FSF_STAT_OFFSET(fsf_scan) }, + { "kernel.fsflush.examined", + { PMDA_PMID(SCLR_FSFLUSH,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, FSF_STAT_OFFSET(fsf_examined) }, + { "kernel.fsflush.locked", + { PMDA_PMID(SCLR_FSFLUSH,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, FSF_STAT_OFFSET(fsf_locked) }, + { "kernel.fsflush.modified", + { PMDA_PMID(SCLR_FSFLUSH,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, FSF_STAT_OFFSET(fsf_modified) }, + { "kernel.fsflush.coalesced", + { PMDA_PMID(SCLR_FSFLUSH,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, FSF_STAT_OFFSET(fsf_coalesce) }, + { "kernel.fsflush.released", + { PMDA_PMID(SCLR_FSFLUSH,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, FSF_STAT_OFFSET(fsf_releases) }, + { "kernel.fsflush.time", + { PMDA_PMID(SCLR_FSFLUSH,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, FSF_STAT_OFFSET(fsf_time) }, + + { "mem.physmem", + { PMDA_PMID(SCLR_SYSINFO,136), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, -1}, + { "mem.freemem", + { PMDA_PMID(SCLR_SYSINFO,137), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, -1}, + { "mem.lotsfree", + { PMDA_PMID(SCLR_SYSINFO,138), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, -1}, + { "mem.availrmem", + { PMDA_PMID(SCLR_SYSINFO,139), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, -1}, + + { "zfs.arc.size", + { PMDA_PMID(SCLR_ARCSTATS,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"size"}, + { "zfs.arc.min_size", + { PMDA_PMID(SCLR_ARCSTATS,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"c_min"}, + { "zfs.arc.max_size", + { PMDA_PMID(SCLR_ARCSTATS,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"c_max"}, + { "zfs.arc.mru_size", + { PMDA_PMID(SCLR_ARCSTATS,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"p"}, + { "zfs.arc.target_size", + { PMDA_PMID(SCLR_ARCSTATS,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"c"}, + { "zfs.arc.misses.total", + { PMDA_PMID(SCLR_ARCSTATS,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"misses"}, + { "zfs.arc.misses.demand_data", + { PMDA_PMID(SCLR_ARCSTATS,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"demand_data_misses"}, + { "zfs.arc.misses.demand_metadata", + { PMDA_PMID(SCLR_ARCSTATS,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"demand_metadata_misses"}, + { "zfs.arc.misses.prefetch_data", + { PMDA_PMID(SCLR_ARCSTATS,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"prefetch_data_misses"}, + { "zfs.arc.misses.prefetch_metadata", + { PMDA_PMID(SCLR_ARCSTATS,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"prefetch_metadata_misses"}, + { "zfs.arc.hits.total", + { PMDA_PMID(SCLR_ARCSTATS,10), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"hits"}, + { "zfs.arc.hits.mfu", + { PMDA_PMID(SCLR_ARCSTATS,11), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"mfu_hits"}, + { "zfs.arc.hits.mru", + { PMDA_PMID(SCLR_ARCSTATS,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"mru_hits"}, + { "zfs.arc.hits.mfu_ghost", + { PMDA_PMID(SCLR_ARCSTATS,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"mfu_ghost_hits"}, + { "zfs.arc.hits.mru_ghost", + { PMDA_PMID(SCLR_ARCSTATS,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"mru_ghost_hits"}, + { "zfs.arc.hits.demand_data", + { PMDA_PMID(SCLR_ARCSTATS,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"demand_data_hits"}, + { "zfs.arc.hits.demand_metadata", + { PMDA_PMID(SCLR_ARCSTATS,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"demand_metadata_hits"}, + { "zfs.arc.hits.prefetch_data", + { PMDA_PMID(SCLR_ARCSTATS,17), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"prefetch_data_hits"}, + { "zfs.arc.hits.prefetch_metadata", + { PMDA_PMID(SCLR_ARCSTATS,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"prefetch_metadata_hits"}, + { "pmda.prefetch.time", + { PMDA_PMID(4095,0), PM_TYPE_U64, PREFETCH_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, -1 }, + { "pmda.prefetch.count", + { PMDA_PMID(4095,1), PM_TYPE_U64, PREFETCH_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1 }, + { "pmda.metric.time", + { PMDA_PMID(4095,2), PM_TYPE_U64, METRIC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, -1 }, + { "pmda.metric.count", + { PMDA_PMID(4095,3), PM_TYPE_U64, METRIC_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1 }, + { "disk.dev.wait.time", + { PMDA_PMID(SCLR_DISK,16), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, KSTAT_IO_OFF(wtime)}, + { "disk.dev.wait.count", + { PMDA_PMID(SCLR_DISK,17), PM_TYPE_U32, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, KSTAT_IO_OFF(wcnt)}, + { "disk.dev.run.time", + { PMDA_PMID(SCLR_DISK,18), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, KSTAT_IO_OFF(rtime)}, + { "disk.dev.run.count", + { PMDA_PMID(SCLR_DISK,19), PM_TYPE_U32, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, KSTAT_IO_OFF(rcnt)}, + + { "disk.all.wait.time", + { PMDA_PMID(SCLR_DISK,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, KSTAT_IO_OFF(wtime)}, + { "disk.all.wait.count", + { PMDA_PMID(SCLR_DISK,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, -1}, + { "disk.all.run.time", + { PMDA_PMID(SCLR_DISK,8), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_NSEC, 0) + }, KSTAT_IO_OFF(rtime)}, + { "disk.all.run.count", + { PMDA_PMID(SCLR_DISK,9), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, -1}, + + { "kernel.fs.read_bytes", + { PMDA_PMID(SCLR_FILESYS,0), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"read_bytes"}, + { "kernel.fs.readdir_bytes", + { PMDA_PMID(SCLR_FILESYS,1), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"readdir_bytes"}, + { "kernel.fs.write_bytes", + { PMDA_PMID(SCLR_FILESYS,2), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"write_bytes"}, + { "kernel.fs.vnops.access", + { PMDA_PMID(SCLR_FILESYS,3), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"naccess"}, + { "kernel.fs.vnops.addmap", + {PMDA_PMID(SCLR_FILESYS,4), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"naddmap"}, + { "kernel.fs.vnops.close", + {PMDA_PMID(SCLR_FILESYS,5), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nclose"}, + { "kernel.fs.vnops.cmp", + {PMDA_PMID(SCLR_FILESYS,6), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ncmp"}, + { "kernel.fs.vnops.create", + {PMDA_PMID(SCLR_FILESYS,7), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ncreate"}, + { "kernel.fs.vnops.delmap", + {PMDA_PMID(SCLR_FILESYS,8), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndelmap"}, + { "kernel.fs.vnops.dispose", + {PMDA_PMID(SCLR_FILESYS,9), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndispose"}, + { "kernel.fs.vnops.dump", + {PMDA_PMID(SCLR_FILESYS,10), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndump"}, + { "kernel.fs.vnops.dumpctl", + {PMDA_PMID(SCLR_FILESYS,11), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndumpctl"}, + { "kernel.fs.vnops.fid", + {PMDA_PMID(SCLR_FILESYS,12), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nfid"}, + { "kernel.fs.vnops.frlock", + {PMDA_PMID(SCLR_FILESYS,13), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nfrlock"}, + { "kernel.fs.vnops.fsync", + {PMDA_PMID(SCLR_FILESYS,14), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nfsync"}, + { "kernel.fs.vnops.getattr", + {PMDA_PMID(SCLR_FILESYS,15), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ngetattr"}, + { "kernel.fs.vnops.getpage", + {PMDA_PMID(SCLR_FILESYS,16), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ngetpage"}, + { "kernel.fs.vnops.getsecattr", + {PMDA_PMID(SCLR_FILESYS,17), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ngetsecattr"}, + { "kernel.fs.vnops.inactive", + {PMDA_PMID(SCLR_FILESYS,18), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ninactive"}, + { "kernel.fs.vnops.ioctl", + {PMDA_PMID(SCLR_FILESYS,19), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nioctl"}, + { "kernel.fs.vnops.link", + {PMDA_PMID(SCLR_FILESYS,20), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nlink"}, + { "kernel.fs.vnops.lookup", + {PMDA_PMID(SCLR_FILESYS,21), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nlookup"}, + { "kernel.fs.vnops.map", + {PMDA_PMID(SCLR_FILESYS,22), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nmap"}, + { "kernel.fs.vnops.mkdir", + {PMDA_PMID(SCLR_FILESYS,23), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nmkdir"}, + { "kernel.fs.vnops.open", + {PMDA_PMID(SCLR_FILESYS,24), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nopen"}, + { "kernel.fs.vnops.pageio", + {PMDA_PMID(SCLR_FILESYS,25), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"npageio"}, + { "kernel.fs.vnops.pathconf", + {PMDA_PMID(SCLR_FILESYS,26), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"npathconf"}, + { "kernel.fs.vnops.poll", + {PMDA_PMID(SCLR_FILESYS,27), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"npoll"}, + { "kernel.fs.vnops.putpage", + {PMDA_PMID(SCLR_FILESYS,28), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nputpage"}, + { "kernel.fs.vnops.read", + {PMDA_PMID(SCLR_FILESYS,29), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nread"}, + { "kernel.fs.vnops.readdir", + {PMDA_PMID(SCLR_FILESYS,30), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nreaddir"}, + { "kernel.fs.vnops.readlink", + {PMDA_PMID(SCLR_FILESYS,31), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nreadlink"}, + { "kernel.fs.vnops.realvp", + {PMDA_PMID(SCLR_FILESYS,32), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrealvp"}, + { "kernel.fs.vnops.remove", + {PMDA_PMID(SCLR_FILESYS,33), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nremove"}, + { "kernel.fs.vnops.rename", + {PMDA_PMID(SCLR_FILESYS,34), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrename"}, + { "kernel.fs.vnops.rmdir", + {PMDA_PMID(SCLR_FILESYS,35), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrmdir"}, + { "kernel.fs.vnops.rwlock", + {PMDA_PMID(SCLR_FILESYS,36), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrwlock"}, + { "kernel.fs.vnops.rwunlock", + {PMDA_PMID(SCLR_FILESYS,37), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrwunlock"}, + { "kernel.fs.vnops.seek", + {PMDA_PMID(SCLR_FILESYS,38), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nseek"}, + { "kernel.fs.vnops.setattr", + {PMDA_PMID(SCLR_FILESYS,39), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsetattr"}, + { "kernel.fs.vnops.setfl", + {PMDA_PMID(SCLR_FILESYS,40), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsetfl"}, + { "kernel.fs.vnops.setsecattr", + {PMDA_PMID(SCLR_FILESYS,41), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsetsecattr"}, + { "kernel.fs.vnops.shrlock", + {PMDA_PMID(SCLR_FILESYS,42), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nshrlock"}, + { "kernel.fs.vnops.space", + {PMDA_PMID(SCLR_FILESYS,43), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nspace"}, + { "kernel.fs.vnops.symlink", + {PMDA_PMID(SCLR_FILESYS,44), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsymlink"}, + { "kernel.fs.vnops.vnevent", + {PMDA_PMID(SCLR_FILESYS,45), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nvnevent"}, + { "kernel.fs.vnops.write", + {PMDA_PMID(SCLR_FILESYS,46), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nwrite"}, + + { "kernel.fstype.read_bytes", + { PMDA_PMID(SCLR_FILESYS,47), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"read_bytes"}, + { "kernel.fstype.readdir_bytes", + { PMDA_PMID(SCLR_FILESYS,48), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"readdir_bytes"}, + { "kernel.fstype.write_bytes", + { PMDA_PMID(SCLR_FILESYS,49), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"write_bytes"}, + { "kernel.fstype.vnops.access", + { PMDA_PMID(SCLR_FILESYS,50), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"naccess"}, + { "kernel.fstype.vnops.addmap", + {PMDA_PMID(SCLR_FILESYS,51), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"naddmap"}, + { "kernel.fstype.vnops.close", + {PMDA_PMID(SCLR_FILESYS,52), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nclose"}, + { "kernel.fstype.vnops.cmp", + {PMDA_PMID(SCLR_FILESYS,53), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ncmp"}, + { "kernel.fstype.vnops.create", + {PMDA_PMID(SCLR_FILESYS,54), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ncreate"}, + { "kernel.fstype.vnops.delmap", + {PMDA_PMID(SCLR_FILESYS,55), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndelmap"}, + { "kernel.fstype.vnops.dispose", + {PMDA_PMID(SCLR_FILESYS,56), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndispose"}, + { "kernel.fstype.vnops.dump", + {PMDA_PMID(SCLR_FILESYS,57), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndump"}, + { "kernel.fstype.vnops.dumpctl", + {PMDA_PMID(SCLR_FILESYS,58), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ndumpctl"}, + { "kernel.fstype.vnops.fid", + {PMDA_PMID(SCLR_FILESYS,59), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nfid"}, + { "kernel.fstype.vnops.frlock", + {PMDA_PMID(SCLR_FILESYS,60), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nfrlock"}, + { "kernel.fstype.vnops.fsync", + {PMDA_PMID(SCLR_FILESYS,61), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nfsync"}, + { "kernel.fstype.vnops.getattr", + {PMDA_PMID(SCLR_FILESYS,62), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ngetattr"}, + { "kernel.fstype.vnops.getpage", + {PMDA_PMID(SCLR_FILESYS,63), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ngetpage"}, + { "kernel.fstype.vnops.getsecattr", + {PMDA_PMID(SCLR_FILESYS,64), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ngetsecattr"}, + { "kernel.fstype.vnops.inactive", + {PMDA_PMID(SCLR_FILESYS,65), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"ninactive"}, + { "kernel.fstype.vnops.ioctl", + {PMDA_PMID(SCLR_FILESYS,66), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nioctl"}, + { "kernel.fstype.vnops.link", + {PMDA_PMID(SCLR_FILESYS,67), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nlink"}, + { "kernel.fstype.vnops.lookup", + {PMDA_PMID(SCLR_FILESYS,68), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nlookup"}, + { "kernel.fstype.vnops.map", + {PMDA_PMID(SCLR_FILESYS,69), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nmap"}, + { "kernel.fstype.vnops.mkdir", + {PMDA_PMID(SCLR_FILESYS,70), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nmkdir"}, + { "kernel.fstype.vnops.open", + {PMDA_PMID(SCLR_FILESYS,71), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nopen"}, + { "kernel.fstype.vnops.pageio", + {PMDA_PMID(SCLR_FILESYS,72), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"npageio"}, + { "kernel.fstype.vnops.pathconf", + {PMDA_PMID(SCLR_FILESYS,73), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"npathconf"}, + { "kernel.fstype.vnops.poll", + {PMDA_PMID(SCLR_FILESYS,74), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"npoll"}, + { "kernel.fstype.vnops.putpage", + {PMDA_PMID(SCLR_FILESYS,75), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nputpage"}, + { "kernel.fstype.vnops.read", + {PMDA_PMID(SCLR_FILESYS,76), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nread"}, + { "kernel.fstype.vnops.readdir", + {PMDA_PMID(SCLR_FILESYS,77), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nreaddir"}, + { "kernel.fstype.vnops.readlink", + {PMDA_PMID(SCLR_FILESYS,78), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nreadlink"}, + { "kernel.fstype.vnops.realvp", + {PMDA_PMID(SCLR_FILESYS,79), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrealvp"}, + { "kernel.fstype.vnops.remove", + {PMDA_PMID(SCLR_FILESYS,80), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nremove"}, + { "kernel.fstype.vnops.rename", + {PMDA_PMID(SCLR_FILESYS,81), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrename"}, + { "kernel.fstype.vnops.rmdir", + {PMDA_PMID(SCLR_FILESYS,82), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrmdir"}, + { "kernel.fstype.vnops.rwlock", + {PMDA_PMID(SCLR_FILESYS,83), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrwlock"}, + { "kernel.fstype.vnops.rwunlock", + {PMDA_PMID(SCLR_FILESYS,84), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nrwunlock"}, + { "kernel.fstype.vnops.seek", + {PMDA_PMID(SCLR_FILESYS,85), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nseek"}, + { "kernel.fstype.vnops.setattr", + {PMDA_PMID(SCLR_FILESYS,86), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsetattr"}, + { "kernel.fstype.vnops.setfl", + {PMDA_PMID(SCLR_FILESYS,87), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsetfl"}, + { "kernel.fstype.vnops.setsecattr", + {PMDA_PMID(SCLR_FILESYS,88), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsetsecattr"}, + { "kernel.fstype.vnops.shrlock", + {PMDA_PMID(SCLR_FILESYS,89), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nshrlock"}, + { "kernel.fstype.vnops.space", + {PMDA_PMID(SCLR_FILESYS,90), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nspace"}, + { "kernel.fstype.vnops.symlink", + {PMDA_PMID(SCLR_FILESYS,91), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nsymlink"}, + { "kernel.fstype.vnops.vnevent", + {PMDA_PMID(SCLR_FILESYS,92), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nvnevent"}, + { "kernel.fstype.vnops.write", + {PMDA_PMID(SCLR_FILESYS,93), PM_TYPE_U64, FSTYPE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, (ptrdiff_t)"nwrite"}, + + { "hinv.cpu.maxclock", + {PMDA_PMID(SCLR_SYSINFO,147), PM_TYPE_64, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, -1, 1, 0, PM_TIME_SEC, 6) + }, (ptrdiff_t)"clock_MHz"}, + { "hinv.cpu.clock", + {PMDA_PMID(SCLR_SYSINFO,148), PM_TYPE_U64, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, -1, 1, 0, PM_TIME_SEC, 0) + }, (ptrdiff_t)"current_clock_Hz"}, + { "hinv.cpu.brand", + {PMDA_PMID(SCLR_SYSINFO, 149), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"brand"}, + { "hinv.cpu.frequencies", + {PMDA_PMID(SCLR_SYSINFO, 150), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"supported_frequencies_Hz"}, + { "hinv.cpu.implementation", + {PMDA_PMID(SCLR_SYSINFO, 151), PM_TYPE_STRING, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"implementation"}, + { "hinv.cpu.chip_id", + {PMDA_PMID(SCLR_SYSINFO, 152), PM_TYPE_64, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"chip_id"}, + { "hinv.cpu.clog_id", + {PMDA_PMID(SCLR_SYSINFO, 153), PM_TYPE_32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"clog_id"}, + { "hinv.cpu.core_id", + {PMDA_PMID(SCLR_SYSINFO, 154), PM_TYPE_64, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"core_id"}, + { "hinv.cpu.pkg_core_id", + {PMDA_PMID(SCLR_SYSINFO, 155), PM_TYPE_64, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"pkg_core_id"}, + { "hinv.cpu.cstate", + {PMDA_PMID(SCLR_SYSINFO, 156), PM_TYPE_32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"current_cstate"}, + { "hinv.cpu.maxcstates", + {PMDA_PMID(SCLR_SYSINFO, 157), PM_TYPE_32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"supported_max_cstates"}, + { "hinv.cpu.ncores", + {PMDA_PMID(SCLR_SYSINFO, 158), PM_TYPE_32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"ncore_per_chip"}, + { "hinv.cpu.ncpus", + {PMDA_PMID(SCLR_SYSINFO, 159), PM_TYPE_32, CPU_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"ncpu_per_chip"}, + + { "disk.dev.errors.soft", + {PMDA_PMID(SCLR_DISK, 21), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Soft Errors"}, + { "disk.dev.errors.hard", + {PMDA_PMID(SCLR_DISK, 22), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Hard Errors"}, + { "disk.dev.errors.transport", + {PMDA_PMID(SCLR_DISK, 23), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Transport Errors"}, + { "disk.dev.errors.media", + {PMDA_PMID(SCLR_DISK, 24), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Media Error"}, + { "disk.dev.errors.recoverable", + {PMDA_PMID(SCLR_DISK, 25), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Recoverable"}, + { "disk.dev.errors.notready", + {PMDA_PMID(SCLR_DISK, 26), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Device Not Ready"}, + { "disk.dev.errors.nodevice", + {PMDA_PMID(SCLR_DISK, 27), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"No Device"}, + { "disk.dev.errors.badrequest", + {PMDA_PMID(SCLR_DISK, 28), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Illegal Request"}, + { "disk.dev.errors.pfa", + {PMDA_PMID(SCLR_DISK, 29), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE), + }, (ptrdiff_t)"Predictive Failure Analysis"}, + { "hinv.disk.vendor", + {PMDA_PMID(SCLR_DISK, 30), PM_TYPE_STRING, DISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"Vendor"}, + { "hinv.disk.product", + {PMDA_PMID(SCLR_DISK, 31), PM_TYPE_STRING, DISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"Product"}, + { "hinv.disk.revision", + {PMDA_PMID(SCLR_DISK, 32), PM_TYPE_STRING, DISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"Revision"}, + { "hinv.disk.serial", + {PMDA_PMID(SCLR_DISK, 33), PM_TYPE_STRING, DISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, (ptrdiff_t)"Serial No"}, + { "hinv.disk.capacity", + { PMDA_PMID(SCLR_DISK,34), PM_TYPE_U64, DISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, (ptrdiff_t)"Size" }, + { "hinv.disk.devlink", + {PMDA_PMID(SCLR_DISK, 35), PM_TYPE_STRING, DISK_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, -1} + + /* remember to add trailing comma before adding more entries ... */ +}; +int metrictab_sz = ARRAY_SIZE(metricdesc); + +pmdaInstid metric_insts[ARRAY_SIZE(metricdesc)]; + +/* + * List of instance domains ... we expect the *_INDOM macros + * to index into this table. + */ +pmdaIndom indomtab[] = { + { DISK_INDOM, 0, NULL }, + { CPU_INDOM, 0, NULL }, + { NETIF_INDOM, 0, NULL }, + { ZPOOL_INDOM, 0, NULL }, + { ZFS_INDOM, 0, NULL }, + { ZPOOL_PERDISK_INDOM, 0, NULL }, + { NETLINK_INDOM}, + { ZFS_SNAP_INDOM }, + { LOADAVG_INDOM, ARRAY_SIZE(loadavg_insts), loadavg_insts}, + { PREFETCH_INDOM, ARRAY_SIZE(prefetch_insts), prefetch_insts}, + { METRIC_INDOM, ARRAY_SIZE(metric_insts), metric_insts}, + { FILESYS_INDOM }, + { FSTYPE_INDOM } +}; + +int indomtab_sz = sizeof(indomtab) / sizeof(indomtab[0]); + +static kstat_ctl_t *kc; +static int kstat_chains_updated; + +kstat_ctl_t * +kstat_ctl_update(void) +{ + if (!kstat_chains_updated) { + if (kstat_chain_update(kc) == -1) { + kstat_chains_updated = 0; + return NULL; + } + kstat_chains_updated = 1; + } + return kc; +} + +void +kstat_ctl_needs_update(void) +{ + kstat_chains_updated = 0; +} + +void +init_data(int domain) +{ + int i; + int serial; + __pmID_int *ip; + + /* + * set up kstat() handle ... failure is fatal + */ + if ((kc = kstat_open()) == NULL) { + fprintf(stderr, "init_data: kstat_open failed: %s\n", osstrerror()); + exit(1); + } + + /* + * Create the PMDA's metrictab[] version of the per-metric table. + * + * Also do domain initialization for each pmid and indom element of + * the metricdesc[] table ... the PMDA table is fixed up in + * libpcp_pmda + */ + if ((metrictab = (pmdaMetric *)malloc(metrictab_sz * sizeof(pmdaMetric))) == NULL) { + fprintf(stderr, "init_data: Error: malloc metrictab [%d] failed: %s\n", + (int)(metrictab_sz * sizeof(pmdaMetric)), osstrerror()); + exit(1); + } + for (i = 0; i < metrictab_sz; i++) { + metrictab[i].m_user = &metricdesc[i]; + metrictab[i].m_desc = metricdesc[i].md_desc; + ip = (__pmID_int *)&metricdesc[i].md_desc.pmid; + ip->domain = domain; + + if (metricdesc[i].md_desc.indom != PM_INDOM_NULL) { + serial = metricdesc[i].md_desc.indom; + metricdesc[i].md_desc.indom = pmInDom_build(domain, serial); + } + metric_insts[i].i_inst = i+1; + metric_insts[i].i_name = (char *)metricdesc[i].md_name; + } + + /* Bless indoms with our own domain - usually pmdaInit will do it for + * us but we need properly setup indoms for pmdaCache which means that + * we have to do it ourselves */ + for (i = 0; i < indomtab_sz; i++) { + __pmindom_int(&indomtab[i].it_indom)->domain = domain; + } + + /* + * initialize each of the methods + */ + for (i = 0; i < methodtab_sz; i++) { + if (methodtab[i].m_init) { + methodtab[i].m_init(1); + } + + prefetch_insts[i].i_inst = i + 1; + prefetch_insts[i].i_name = (char *)methodtab[i].m_name; + } +} diff --git a/src/pmdas/solaris/disk.c b/src/pmdas/solaris/disk.c new file mode 100644 index 0000000..70261c2 --- /dev/null +++ b/src/pmdas/solaris/disk.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2010 Max Matveev. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" +#include + +#define SOLARIS_PMDA_TRACE (DBG_TRACE_APPL0|DBG_TRACE_APPL2) + +typedef struct { + int fetched; + int err; + kstat_t *ksp; + kstat_io_t iostat; + kstat_t *sderr; + int sderr_fresh; +} ctl_t; + +static di_devlink_handle_t devlink_hndl = DI_LINK_NIL; +static di_node_t di_root = DI_NODE_NIL; + +static ctl_t * +getDiskCtl(pmInDom dindom, const char *name) +{ + ctl_t *ctl = NULL; + int inst; + int rv = pmdaCacheLookupName(dindom,name, &inst, (void **)&ctl); + + if (rv == PMDA_CACHE_ACTIVE) + return ctl; + + if ((rv == PMDA_CACHE_INACTIVE) && ctl) { + rv = pmdaCacheStore(dindom, PMDA_CACHE_ADD, name, ctl); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot reactivate cached data for disk '%s': %s\n", + name, pmErrStr(rv)); + return NULL; + } + } else { + if ((ctl = (ctl_t *)calloc(1, sizeof(ctl_t))) == NULL) { + __pmNotifyErr(LOG_WARNING, + "Out of memory to keep state for disk '%s'\n", + name); + return NULL; + } + + rv = pmdaCacheStore(dindom, PMDA_CACHE_ADD, name, ctl); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot cache data for disk '%s': %s\n", + name, pmErrStr(rv)); + free(ctl); + return NULL; + } + } + return ctl; +} + +static void +disk_walk_chains(pmInDom dindom) +{ + kstat_t *ksp; + kstat_ctl_t *kc; + + if ((kc = kstat_ctl_update()) == NULL) + return; + + for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + ctl_t *ctl; + + if ((strcmp(ksp->ks_class, "disk") == 0) && + (ksp->ks_type == KSTAT_TYPE_IO)) { + if ((ctl = getDiskCtl(dindom, ksp->ks_name)) == NULL) + continue; + + ctl->ksp = ksp; + ctl->fetched = 0; + } else if (strcmp(ksp->ks_class, "device_error") == 0) { + char *comma; + char modname[KSTAT_STRLEN]; + + strcpy(modname, ksp->ks_name); + if ((comma = strchr(modname, ',')) == NULL) + continue; + + *comma = '\0'; + if ((ctl = getDiskCtl(dindom, modname)) == NULL) + continue; + ctl->sderr = ksp; + ctl->sderr_fresh = 0; + } + } +} + +void +disk_init(int first) +{ + pmInDom dindom = indomtab[DISK_INDOM].it_indom; + + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; + + pmdaCacheOp(dindom, PMDA_CACHE_LOAD); + disk_walk_chains(dindom); + pmdaCacheOp(dindom, PMDA_CACHE_SAVE); +} + +void +disk_prefetch(void) +{ + if (di_root != DI_NODE_NIL) { + di_fini(di_root); + di_root = DI_NODE_NIL; + } + + if (devlink_hndl != DI_LINK_NIL) { + di_devlink_fini(&devlink_hndl); + devlink_hndl = DI_LINK_NIL; + } + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_INACTIVE); + disk_walk_chains(indomtab[DISK_INDOM].it_indom); + pmdaCacheOp(indomtab[DISK_INDOM].it_indom, PMDA_CACHE_SAVE); +} + +static __uint64_t +disk_derived(pmdaMetric *mdesc, int inst, const kstat_io_t *iostat) +{ + pmID pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint64_t val; + + pmid = mdesc->m_desc.pmid; + ip->domain = 0; + +// from kstat_io_t ... +// +// u_longlong_t nread; /* number of bytes read */ +// u_longlong_t nwritten; /* number of bytes written */ +// uint_t reads; /* number of read operations */ +// uint_t writes; /* number of write operations */ +// + + switch (pmid) { + case PMDA_PMID(SCLR_DISK,2): /* disk.all.total */ + case PMDA_PMID(SCLR_DISK,12): /* disk.dev.total */ + val = iostat->reads + iostat->writes; + break; + + case PMDA_PMID(SCLR_DISK,5): /* disk.all.total_bytes */ + case PMDA_PMID(SCLR_DISK,15): /* disk.dev.total_bytes */ + val = iostat->nread + iostat->nwritten; + break; + + /* iostat->wcnt and iostat->rcnt are 32 bit intergers, + * these two metrics must be derived because the metrics + * are using 64 bit integers to avoid overflows during + * accumultion */ + case PMDA_PMID(SCLR_DISK,7): /* disk.all.wait.count */ + val = iostat->wcnt; + break; + case PMDA_PMID(SCLR_DISK,9): /* disk.all.run.time */ + val = iostat->rcnt; + break; + + default: + fprintf(stderr, "disk_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + /* desperate */ + fprintf(stderr, "disk_derived: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, (unsigned long long)val); + } +#endif + + return val; +} + +static int +fetch_disk_data(kstat_ctl_t *kc, const pmdaMetric *mdesc, ctl_t *ctl, + const char *diskname) +{ + if (ctl->fetched == 1) + return 1; + + if (ctl->ksp == NULL) + return 0; + + if ((kstat_read(kc, ctl->ksp, &ctl->iostat) == -1)) { + if (ctl->err == 0) { + __pmNotifyErr(LOG_WARNING, + "Error: disk_fetch(pmid=%s disk=%s ...) - " + "kstat_read(kc=%p, ksp=%p, ...) failed: %s\n", + pmIDStr(mdesc->m_desc.pmid), diskname, + kc, ctl->ksp, osstrerror()); + } + ctl->err++; + ctl->fetched = -1; + return 0; + } + + ctl->fetched = 1; + if (ctl->err != 0) { + __pmNotifyErr(LOG_INFO, + "Success: disk_fetch(pmid=%s disk=%s ...) " + "after %d errors as previously reported\n", + pmIDStr(mdesc->m_desc.pmid), diskname, ctl->err); + ctl->err = 0; + } + + return 1; +} + +static int +get_devlink_path(di_devlink_t devlink, void *arg) +{ + const char **p = arg; + *p = di_devlink_path(devlink); + return DI_WALK_TERMINATE; +} + +static int +fetch_disk_devlink(const kstat_t *ksp, pmAtomValue *atom) +{ + di_node_t n; + + if (di_root == DI_NODE_NIL) { + if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) + return 0; + } + + if (devlink_hndl == DI_LINK_NIL) { + if ((devlink_hndl = di_devlink_init(NULL, DI_MAKE_LINK)) == DI_LINK_NIL) + return 0; + } + + if ((n = di_drv_first_node(ksp->ks_module, di_root)) == DI_NODE_NIL) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + fprintf(stderr,"No nodes for %s: %s\n", + ksp->ks_name, osstrerror()); + } +#endif + return 0; + } + + do { + if (di_instance(n) == ksp->ks_instance) { + di_minor_t minor = di_minor_next(n, DI_MINOR_NIL); + char *path; + char *devlink = NULL; + + if (minor == DI_MINOR_NIL) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + fprintf (stderr, "No minors of %s: %s\n", + ksp->ks_name, osstrerror()); + } +#endif + return 0; + } + path = di_devfs_minor_path(minor); + di_devlink_walk(devlink_hndl, NULL, path, 0, &devlink, + get_devlink_path); + di_devfs_path_free(path); + + if (devlink) { + atom->cp = devlink; + return 1; + } + return 0; + } + n = di_drv_next_node(n); + } while (n != DI_NODE_NIL); + return 0; +} + +static int +get_instance_value(pmdaMetric *mdesc, pmInDom dindom, int inst, + pmAtomValue *atom) +{ + ctl_t *ctl; + char *diskname; + uint64_t ull; + ptrdiff_t offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + kstat_ctl_t *kc; + + if ((kc = kstat_ctl_update()) == NULL) + return 0; + + if (pmdaCacheLookup(dindom, inst, &diskname, + (void **)&ctl) != PMDA_CACHE_ACTIVE) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + fprintf(stderr, + "Unexpected cache result - instance %d " + "is not active in disk indom cache\n", + inst); + } +#endif + return 0; + } + + if (offset == -1) { + if (pmid_item(mdesc->m_desc.pmid) == 35) { /* hinv.disk.devlink */ + return fetch_disk_devlink(ctl->ksp, atom); + } + if (!fetch_disk_data(kc, mdesc, ctl, diskname)) + return 0; + ull = disk_derived(mdesc, inst, &ctl->iostat); + } else if (offset > sizeof(ctl->iostat)) { /* device_error */ + if (ctl->sderr) { + kstat_named_t *kn; + char * m = (char *)offset; + + if (!ctl->sderr_fresh) { + ctl->sderr_fresh = (kstat_read(kc, ctl->sderr, NULL) != -1); + + if (!ctl->sderr_fresh) + return 0; + } + + if ((kn = kstat_data_lookup(ctl->sderr, m)) == NULL) { +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) + fprintf(stderr, "No %s in %s\n", m, diskname); +#endif + return 0; + } + + return kstat_named_to_pmAtom(kn, atom); + } + return 0; + } else { + char *iop = ((char *)&ctl->iostat) + offset; + if (!fetch_disk_data(kc, mdesc, ctl, diskname)) + return 0; + if (mdesc->m_desc.type == PM_TYPE_U64) { + __uint64_t *ullp = (__uint64_t *)iop; + ull = *ullp; +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + /* desperate */ + fprintf(stderr, "disk_fetch: pmid %s inst %d val %llu\n", + pmIDStr(mdesc->m_desc.pmid), inst, + (unsigned long long)*ullp); + } +#endif + } + else { + __uint32_t *ulp = (__uint32_t *)iop; + ull = *ulp; +#ifdef PCP_DEBUG + if ((pmDebug & SOLARIS_PMDA_TRACE) == SOLARIS_PMDA_TRACE) { + /* desperate */ + fprintf(stderr, "disk_fetch: pmid %s inst %d val %u\n", + pmIDStr(mdesc->m_desc.pmid), inst, *ulp); + } +#endif + } + } + + if (mdesc->m_desc.type == PM_TYPE_U64) { + /* export as 64-bit value */ + atom->ull += ull; + } + else { + /* else export as a 32-bit */ + atom->ul += (__uint32_t)ull; + } + + return 1; +} + +int +disk_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + int i; + pmInDom dindom = indomtab[DISK_INDOM].it_indom; + + if (pmid_item(mdesc->m_desc.pmid) == 20) { /* hinv.ndisk */ + i = pmdaCacheOp(dindom, PMDA_CACHE_SIZE_ACTIVE); + if (i < 0) { + return 0; + } else { + atom->ul = i; + return 1; + } + } + + memset(atom, 0, sizeof(*atom)); + + if (inst == PM_IN_NULL) { + pmdaCacheOp(dindom,PMDA_CACHE_WALK_REWIND); + while ((i = pmdaCacheOp(dindom, PMDA_CACHE_WALK_NEXT)) != -1) { + if (get_instance_value(mdesc, dindom, i, atom) == 0) + return 0; + } + return 1; + } + + return get_instance_value(mdesc, dindom, inst, atom); +} diff --git a/src/pmdas/solaris/help b/src/pmdas/solaris/help new file mode 100644 index 0000000..4940247 --- /dev/null +++ b/src/pmdas/solaris/help @@ -0,0 +1,729 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Solaris PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ kernel.all.cpu.idle Amount of time CPUs were idle +@ kernel.all.cpu.user Amount of time spent executing userspace tasks +@ kernel.all.cpu.sys Amount of time spent executing kernel code +@ kernel.all.cpu.wait.total Amount of time CPU spent waiting for events +@ kernel.percpu.cpu.user Amount of time spent executing userspace tasks by each CPU +@ kernel.percpu.cpu.idle Amount of time each CPU was idle +@ kernel.percpu.cpu.sys Amount of time each CPU spent executing kernel code +@ kernel.percpu.cpu.wait.total Amount of time each CPU spent waiting for events +@ disk.all.read Number of read requests aggregated across all disks +@ disk.all.write Number of write requests aggregated across all disks +@ disk.all.total Number of IO requests aggregated across all disks +@ disk.all.read_bytes Number of bytes read from all disks +@ disk.all.write_bytes Number of bytes written to all disk +@ disk.all.total_bytes Number of bytes transferred to and from all disks +@ disk.dev.read Number of read requests for each individual disk +@ disk.dev.write Number of write requests for each individual disk +@ disk.dev.total Number of IO requests for each individual disk +@ disk.dev.read_bytes Number of bytes read from each individual disk +@ disk.dev.write_bytes Number of bytes written to each individual disk +@ disk.dev.total_bytes Number of bytes transferred to and from each individual disk +@ network.interface.mtu Maximum Transmission Unit of a network interface +Maximum Transmision Unit is the largest size of IP datagram which can be +transferred over the data link. +@ network.interface.in.bytes Number of bytes received by a network interface +@ network.interface.in.errors Number of receive errors per network interface +Number of receive errors per network interface. The errors counted towards +this metric are: IP header errors, packets larger then the link MTU, packets +delivered to the unknown address, packets sent to the unknown IP protocol, +truncated packets, packets discarded due to not having a route for +the destination. +@ network.interface.in.drops Number of packets droped by a network interface +Number of packets discared due to lack of space during the input processing. +@ network.interface.in.delivers Number of packets delivered to ULPs +Number of packets delivered for further processing by the upper-layer +protocols. +@ network.interface.in.bcasts Number of broadcast packets received by a network interface +@ network.interface.in.packets Number of IP packets received by a network interface +@ network.interface.in.mcasts Number of multicast packets received by a network interface +@ network.interface.out.packets Number of packets sent by a network interface +@ network.interface.out.bytes Number of bytes sent by a network interface +@ network.interface.out.errors Number of send errors per network interface +@ network.interface.out.bcasts Number of broadcast packets sent by a network interface +@ network.interface.out.mcasts Number of multicast packets sent by a network interface +@ network.interface.out.drops Number of packets discared by a network interface +Number of packets discared due to lack of space during the output processing. +@ network.udp.ipackets Number of UDP packets received +@ network.udp.opackets Number of UDP packets sent +@ network.udp.ierrors Number of receive errors in UDP processing +@ network.udp.oerrors Number of send erros in UDP processing +@ network.udp.noports Number of UDP packets received on unknown UDP port +Number of UDP packets received for which here is no port can be found. +This counter is reported on the per-interface basis and aggregated by the PMDA. + +@ network.udp.overflows Number of UDP packets droped due to queue overflow +Number of UDP packets droped due to queue overflow. +This counter is reported on the per-interface basis and aggregated by the PMDA. + +@zpool.capacity Total capacity of a zpool in bytes +@zpool.used Total space used on a pool +@zpool.checksum_errors Number of checksum errors per zpool +@zpool.self_healed Number of bytes healed +@zpool.in.bytes Counter of bytes read from a zpool +@zpool.out.bytes Counter of bytes written to a zpool +@zpool.in.ops Counter of reads per zpool +@zpool.out.ops Counter of writes per zpool +@zpool.in.errors Counter of read errors per zpool +@zpool.out.errors Counter of write errors per zpool +@zpool.state Current state of zpool +@zpool.state_int vs_aux << 8 | vs_state + +@zfs.available Amount of space available to the dataset +The amount of space available to the dataset (a filesystem, +a snapshot or a volume) and all its children. This is usually +the amount of space available in the zpool which houses the +dataset. + +@zfs.used.total Amount of space used by the dataset and its children +The amount of space consumed by the filesystem, snapshot or +volume and all its children. This amount does not include +any reservation made by the dataset itself but do include +the reservation of the children. + +@zfs.used.byme Amount of space used by the dataset itself. +This amount exclude any space used by the children of this dataset +or any of its snapshots. + +@zfs.used.bysnapshots Amount of space used by the snapshots of the dataset +The amount of space consumed by the snapshots of this dataset. + +@zfs.used.bychildren Amount of space used by decendents of the dataset +The amount of space consumed by all the decendants of this dataset. + +@zfs.quota Maximum amount of space a dataset can use +Quotas are used to restrict the growth of the datasets. If +the quota is set to 0 then the size of the dataset is limited only +by the size of the pool which houses this dataset. + +@zfs.reservation Minimum amount of space guaranteed to a dataset +The amount of space which dataset and its decendents are guaranteed +to be available for them to use. This amount of taken off the quota +of the parent of the dataset. + +@zfs.compression Compression ratio of the dataset +Compression ratio is expressed as multiplier. To estimate how much data +will be used by the uncompressed data multiply the amount of space used +by the dataset by the compression ratio. + +@zfs.copies Number of redundant copies of data +The number of redundant copies does not include any copies made as +part of the pool redundancy. + +@zfs.recordsize Recommendend block size for files in filesystems +By using recommended block size applications which deal with fixed size +records can improve I/O performance. + +@zfs.used.byrefreservation Space used by refreservation +The amount of space used by a refreservation set on this +filesystem, which would be freed if the refreservation was +removed. + +@zfs.refreservation Minimum amount of space guaranteed to a filesystem +The minimum amount of space guaranteed to a dataset, not +including its descendents. Unlike reservation refreservation is +counted towards the total used space of a dataset. + +@zfs.refquota Amount of space a filesystem can consume +The hard limit on the amount of space a filesystem but not its descendants +can consume from the pool. + +@zfs.referenced Amount of space referenced by the filesystem +The amount of data that is accessible by the filesystem. The data +may be shared with other datasets in the pool. + +@zfs.nsnapshots Number of snapshots in the filesystem + +@zfs.snapshot.compression Compression ratio of the data in the snapshot +Compression ratio is expressed as multiplier. To estimate how much data +will be used by the uncompressed data multiply the amount of space used +by the snapshot by the compression ratio. + +@zfs.snapshot.used Amount of space used by the snapshot + +@zfs.snapshot.referenced Amount of space referenced by the snapshot +The amount of data that is accessible by the snapshot. The data +may be shared with other datasets in the filesystem. + + +@zpool.perdisk.state Current state per disk in zpool +@zpool.perdisk.state_int vs_aux << 8 | vs_state +@zpool.perdisk.checksum_errors Number of checksum errors per disk in zpool +@zpool.perdisk.self_healed Number of bytes healed per disk in zpool +@zpool.perdisk.in.errors Counter of read errors per disk in zpool +@zpool.perdisk.out.errors Counter of write errors per disk in zpool + +@network.link.in.errors Number of input errors per link +Counts input errors per link +@network.link.in.packets Numbe of datagrams received by a link +@network.link.in.bytes Number of bytes received by a link +Counts number of bytes received by a link. For the physical links +this is the raw counter of bytes received, for the aggregated links +this is the number of bytes received by all links in the aggregation +group +@network.link.in.bcasts Number of broadcast datagrams received by a link +@network.link.in.mcasts Number of multicast datagrams +Counts multicast datagram recieved by a link. +@network.link.in.nobufs Number of inpit packets discared +Counts number of packets discared because of failure to allocate buffers +@network.link.out.errors Number of output errors per link +@network.link.out.packets Number of packets sent from a link +@network.link.out.bytes Number of bytes sent from a link +@network.link.out.bcasts Number of broadcast datagrams sent from a link +@network.link.out.mcasts Number of multicast datagrams sent from a link +@network.link.out.nobufs Number of output packets discared +Counts number of packets discared because of failure to allocate buffers +@network.link.collisions Number of collisions detected per link +@network.link.state Link state +1 - Link is up, 2 - Link is down, 0 - unknown state +@network.link.duplex Link duplex +1 - Half duplex, 2 - Full duplex +@network.link.speed Link speed in bytes per second +@hinv.pagesize Memory page size +The memory page size of the running kernel in bytes. +@hinv.physmem Total physical system memory +Total physical system memory size rounded down to the nearest page size +boundary +@pmda.uname identity and type of current system +Identity and type of current system. The concatenation of the values +returned from utsname(2), also similar to uname -a. +@kernel.fsflush.scanned Number of pages scanned by fsflush daemon +@kernel.fsflush.examined Number of pages examined by fsflush daemon +@kernel.fsflush.coalesced Number of pages coalesced into larger page +@kernel.fsflush.modified Number of modified pages written to disk +@kernel.fsflush.locked Number of pages locked by fsflush daemon +Pages which were considered to be on interest for further examination +are locked before deciding if they could be coalesced, released or flushed +to disk. +@kernel.fsflush.released Number of free pages released by fsflush daemon +@kernel.fsflush.time Amount of time fsflush daemon spent doing its work +@mem.physmem Total physical system memory +Total physical system memory size rounded down to the nearest page size +boundary. This metric is the same as hinv.physmem but uses different +units. +@mem.freemem Amount of free memory in the system +@mem.lotsfree Paging theshold +If freemem fails below the lostfree threshold then paging out daemon +starts its activity. Default value for lotsfree is 1/64 of physical memory +or 512K (which ever is larger). +@mem.availrmem Amount of resident memory in the system + +@kernel.all.io.bread Physical block reads across all CPUs +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.all.io.bwrite Physical block writes across all CPUs +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.all.io.lread Logical block reads across all CPUs +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.all.io.lwrite Logical block writes across all CPUs +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.all.io.phread Raw I/O reads across all CPUs +@kernel.all.io.phwrite Raw I/O writes across all CPUs +@kernel.all.io.intr Device interrupts across all CPUs + +@kernel.percpu.io.bread Physical block reads +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.percpu.io.bwrite Physical block writes +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.percpu.io.lread Logical block reads +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.percpu.io.lwrite Logical block writes +This metric is only updated if reading or writing to UFS mounted filesystems, +reads and writes to ZFS do not update this metric. +@kernel.percpu.io.phread Raw I/O reads +@kernel.percpu.io.phwrite Raw I/O writes +@kernel.percpu.io.intr Device interrupts + +@hinv.ncpu Number of CPUs in the system +@hinv.ndisk Number of disks in the system + +@kernel.all.trap Traps across all CPUs +@kernel.all.pswitch Context switches across all CPUs +@kernel.all.syscall Total number of system calls across all CPUs +@kernel.all.sysexec Total number of calls from exec(2) family across all CPUs +@kernel.all.sysfork Total number of new processes created across all CPUs +@kernel.all.sysvfork Total number of new processes created across all CPUs +Unlike fork vfork does not copy all the virtual memory of the parent +process into the child process and is mostly used to create new system context +for execve(2). vfork(2) calls are not counted towards kernel.all.sysfork. +@kernel.all.sysread Total number of system calls from read(2) family across all CPUs +@kernel.all.syswrite Total number of system calls from write (2) family across all CPUs + +@kernel.percpu.trap Traps on each CPUs +@kernel.percpu.pswitch Context switches on each CPUs +@kernel.percpu.syscall Total number of system calls on each CPU +@kernel.percpu.sysexec Total number of calls from exec(2) family on each CPU +@kernel.percpu.sysfork Total number of new processes created on each CPU +@kernel.percpu.sysvfork Total number of new processes created on each CPU +Unlike fork vfork does not copy all the virtual memory of the parent +process into the child process and is mostly used to create new system context +for execve(2). vfork(2) calls are not counted towards kernel.percpu.sysfork. +@kernel.percpu.sysread Total number of system calls from read(2) family on each CPU +@kernel.percpu.syswrite Total number of system calls from write (2) family on each CPU + +@kernel.all.load Classic load average for 1, 5 and 15 minute intervals + +@kernel.all.cpu.wait.io Time spent waiting for I/O across all CPUs +This metric is not updated by OpenSolaris kernel. +@kernel.all.cpu.wait.pio Time spent wait for polled I/O across all CPUs +This metric is not updated by OpenSolaris kernel. +@kernel.all.cpu.wait.swap Time spent wait for swap across all CPUs +This metric is not updated by OpenSolaris kernel. +@kernel.percpu.cpu.wait.io Time spent waiting for I/O on per-CPU basis +This metric is not updated by OpenSolaris kernel. +@kernel.percpu.cpu.wait.pio Time spent waiting for polled I/O on per-CPU basis +This metric is not updated by OpenSolaris kernel. +@kernel.percpu.cpu.wait.swap Time spent waiting swap on per-CPU basis +This metric is not updated by OpenSolaris kernel. + +@zfs.arc.size Total amount of memory used by ZFS ARC +@zfs.arc.min_size Lower limit of them amount of memory for ZFS ARC +@zfs.arc.max_size Upper limit of the amount of memory for ZFS ARC +The default is to use 7/8 of total physical memory. +@zfs.arc.mru_size Amount of memory used by the most recently used pages +@zfs.arc.target_size "Ideal" size of the cached based on aging +@zfs.arc.hits.total Number of times data is found in the cache +@zfs.arc.hits.mfu Number of times data is found in the most frequently used buffers +@zfs.arc.hits.mru Number of times data is found in the most recently used buffers +@zfs.arc.hits.mfu_ghost Number of times MFU ghost buffer is accessed +A ghost buffer is a buffer which is no longer cached but is still +linked into the hash. +@zfs.arc.hits.mru_ghost Number of times MRU ghost buffer is accessed +A ghost buffer is a buffer which is no longer cached but is still +linked into the hash. +@zfs.arc.hits.demand_data Number of times file data is found in the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.hits.demand_metadata Number of times filesystem metadata is found in the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.hits.prefetch_data Number of times speculative request for data is satisfied from the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.hits.prefetch_metadata Number of times speculative request for metadata is satisfied from the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.misses.total Number of times the data is not found in the cache +@zfs.arc.misses.demand_data Number of times file data is not found in the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.misses.demand_metadata Number of times filesystem metadata is not found in the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.misses.prefetch_data Number of times speculatively accessed file data is not found in the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@zfs.arc.misses.prefetch_metadata Number of times speculatively accessed filesystem metadata is not found in the cache +ARC statistics provide separate counters for demand vs prefetch +and data vs metadata accesses: demand is a result of the direct +request for a particular data, prefetch is a result of speculative +request for a particular data. +@pmda.prefetch.time Amount of time spent extracting information about a group of metrics +Each metric belongs to a prefetch group. When a client asks for the metric +to be fetched the information for the group must be extracted from the kernel. +@pmda.prefetch.count Number of times each group of metrics was updated + +@pmda.metric.time Amount of time spent extracting information about individual metrics +Requesting multiple instances of the same metrics counts against the metric +itself and not against the individual instances +@pmda.metric.count Number of times individual metrics have been fetched +Requesting multiple instances of the same metrics counts as multiple hits +against the metric itself + +@disk.all.wait.time Amount of time IO requests spent waiting for service +Amount of time IO transactions spent waiting to be serviced, i.e. the +transaction has been accepted for processing but for which the processing +has not yet begun. Each transaction waiting for processing adds to +to the total time which means that if multiple transactions are waiting then +total time for the sampling interval may be larger then the interval. + +@disk.dev.wait.time Amount of time IO requests spent waiting for service +Amount of time IO transactions spent waiting to be serviced, i.e. the +transaction has been accepted for processing but for which the processing +has not yet begun. Each transaction waiting for processing adds to +to the total time which means that if multiple transactions are waiting then +total time for the sampling interval may be larger then the interval. + +@disk.all.wait.count Number of transactions waiting to be serviced +Number of transactions accepted for processing but for which the processing +has not yet begun. +@disk.dev.wait.count Number of transactions waiting to be serviced +Number of transactions accepted for processing but for which the processing +has not yet begun. + +@disk.all.run.time Amount of time spent processing IO requests +@disk.dev.run.time Amount of time spent processing IO requests +@disk.all.run.count Number of transactions being processed +@disk.dev.run.count Number of transactions being processed + + +# from i86pc/os/cpuid.c +# /* +# * 8bit APIC IDs on dual core Pentiums +# * look like this: +# * +# * +-----------------------+------+------+ +# * | Physical Package ID | MC | HT | +# * +-----------------------+------+------+ +# * <------- chipid --------> +# * <------- coreid ---------------> +# * <--- clogid --> +# * <------> +# * pkgcoreid +# * +# * Where the number of bits necessary to +# * represent MC and HT fields together equals +# * to the minimum number of bits necessary to +# * store the value of cpi->cpi_ncpu_per_chip. +# * Of those bits, the MC part uses the number +# * of bits necessary to store the value of +# * cpi->cpi_ncore_per_chip. +# */ +# +@hinv.cpu.brand Marketing name of CPU +@hinv.cpu.clock Current CPU clock frequency +On CPUs which support dynamic clock frequency changes current clock frequency +could differ from the nominal ("maximum") clock frequency specified by +the manufacturer. +@hinv.cpu.maxclock Maximum clock frequency supported by CPU +Nominal CPU clock frequency as specified by the manufacturer. +@hinv.cpu.frequencies List of clock frequencies supported by CPU +@hinv.cpu.implementation Details of CPU implementation +@hinv.cpu.chip_id Chip or Socket identifier of the CPU +Logical CPUs can share single chip identifier +@hinv.cpu.clog_id Logical core identifier +Logical cores identifier combines identifiers of the CPU core and +virtual CPU identifier (aka hyperthread identifier). +@hinv.cpu.core_id CPU core identifier +CPU core identifire combines chip identifier and per-chip core +identifier. If cores support more the one virtual CPU per core +then same core identifier is shared across several virtual +CPUs. +@hinv.cpu.pkg_core_id Per-chip core identifier +This identifier is used to identify individual cores within the +package. If a core support more the one virtual CPU per core +then same core identifier is shared across several virtual +CPUs. +@hinv.cpu.cstate Current CPU idle state +@hinv.cpu.maxcstates Maximum number of idle state supported by the CPU +Information about cstate is available in kstat(1m). +@hinv.cpu.ncores Number of CPU cores per physical chip +@hinv.cpu.ncpus Number of virtual CPUs per physical chip + +@disk.dev.errors.soft Number of soft errors per device +@disk.dev.errors.hard Number of hard errors per device +@disk.dev.errors.transport Number of transport errors per device +@disk.dev.errors.media Number of media errors per device +@disk.dev.errors.recoverable Number of recoverable errors per device +@disk.dev.errors.notready Number of times device reported as not ready +@disk.dev.errors.nodevice Number of times device was found missing +@disk.dev.errors.badrequest Number of illegal requests per device +@disk.dev.errors.pfa Number of times failure prediction threshold has been exceeded +@hinv.disk.vendor Device Vendor +Can be reported as ATA if SATA device is behind SAS expander +@hinv.disk.product Device name +Vendor's device name (up-to 16 characters long) +@hinv.disk.revision Device Revision +@hinv.disk.serial Device Serial Number +@hinv.disk.capacity Device Capacity +For removable devices capacity of the media is reported. + +@kernel.fs.vnops.access Number of VOP_ACCESS calls per filesystem +VOP_ACCESS is used by access(2) system call. +@kernel.fs.vnops.addmap Number of VOP_ADDMAP calls per filesystem +VOP_ADDMAP is used to manage reference counting of the vnode used by +mmap(2) operations. +@kernel.fs.vnops.close Number of VOP_CLOSE calls per filesystem +VOP_CLOSE is called every time a close(2) system call is called +@kernel.fs.vnops.cmp Number of VOP_CMP calls per filesystem +VOP_CMP is used to check if two vnodes are "equal" to each other, i.e. +both refer to the same filesystem object. +@kernel.fs.vnops.create Number of VOP_CREATE calls per filesystem +VOP_CREATE is used to create regular files and device or FIFO nodes. +@kernel.fs.vnops.delmap Number of VOP_DELMAP calls +VOP_DELMAP is used to destroy a previously created memory-mapped region +of a file. +@kernel.fs.vnops.dispose Number ot VOP_DISPOSE calls per filesystem +VOP_DISPOSE is used to dispose(free or invalidate) of a page associated +with a file. +@kernel.fs.vnops.dump Number of VOP_DUMP calls per filesystem +VOP_DUMP is used to transfer data from the frozen kernel directly +to the dump device +@kernel.fs.vnops.dumpctl Number of VOP_DUMPCTL calls per filesystem +VOP_DUMPCTL sets up context used by VOP_DUMP call. It is used to +allocate, free or search for data blocks on the dump device. +@kernel.fs.vnops.fid Number of VOP_FID calls per filesystem +VOP_FID is used to get file identifier which can be used instead of the +file name in some operations. NFS server is one known user of this vnode +operation. +@kernel.fs.vnops.frlock Number of VOP_FRLOCK calls per filesystem +VOP_FRLOCK is used to implement file record locking used by flock(2) +@kernel.fs.vnops.fsync Number of VOP_FSYNC calls per filesystem +VOP_FSYNC is used to implement fsync(2) system call which flushes +data for a specific file to disk. +@kernel.fs.vnops.getattr Number of VOP_GETATTR calls per filesystem +VOP_GETATTR is used to extract vnode attributes. It use used as part of +many system calls which manipulate file attributes, e.g. chmod(2), stat(2), +utimes(2) etc. +@kernel.fs.vnops.getpage Number of VOP_GETPAGE calls per filesystem +VOP_GETPAGE is used to allocate pages (could be several at a time) to cover +a region in a file. +@kernel.fs.vnops.getsecattr Number of VOP_GETSECATTR calls per filesystem +VOP_GETSECATTR used to extract ACL entires associated with a file. +@kernel.fs.vnops.inactive Number of VOP_INACTIVE calls per filesystem +VOP_INACTIVE is used to destroy vnode before it is removed from the +cache or reused. +@kernel.fs.vnops.ioctl Number of VOP_IOCTL calls per filesystem +VOP_IOCTL is used to implement ioctl(2) system call. +@kernel.fs.vnops.link Number of VOP_LINK calls per filesystem +VOP_LINK is used to implement support for hard links +@kernel.fs.vnops.lookup Number of VOP_LOOKUP calls per filesystem +VOP_LOOKUP is used to translate filename to vnode. +@kernel.fs.vnops.map Number of VOP_MAP calls per filesystem +VOP_MAP is used to create a new memory-mapped region of a file +@kernel.fs.vnops.mkdir Number of VOP_MKDIR calls per filesystem +VOP_MKDIR is used to create directories +@kernel.fs.vnops.open Number of VOP_OPEN calls per filesystem +VOP_OPEN is called every time open(2) system call is called. +@kernel.fs.vnops.pageio Number of VOP_PAGEIO calls per filesystem +VOP_PAGEIO is similar to VOP_GETPAGE and VOP_PUTPAGE and can be used when +either of the other two are less efficient, e.g. in the case when pages +will be reused after the IO is done. +@kernel.fs.vnops.pathconf Number of VOP_PATHCONF calls per filesystem +VOP_PATHCONF is used to obtain information about filesystem's parameters +reported by pathconf(2) system call +@kernel.fs.vnops.poll Number of VOP_POLL calls per filesystem +VOP_POLL is used to implement pool(2) system call +@kernel.fs.vnops.putpage Number of VOP_PUTPAGE calls per filesystem +VOP_PUTPAGE is used to release pages which have been used to hold +data from a file +@kernel.fs.vnops.read Number of VOP_READ calls per filesystem +VOP_READ is used to implement read(2) system call +@kernel.fs.vnops.readdir Number of VOP_READDIR calls per filesystem +VOP_READDIR is used to read directory entries +@kernel.fs.vnops.readlink Number of VOP_READLINK calls per filesystem +VOP_READLINK is used to read the information about the target of the symbolic +link +@kernel.fs.vnops.realvp Number of VOP_REALVP calls per filesystem +VOP_REALVP is used to traverse stacking filesystems and extract information +about the vnode which refers to the "real" filesystem object. +@kernel.fs.vnops.remove Number of VOP_REMOVE calls per filesystem +VOP_REMOVE is used to remove entires from a directory. +@kernel.fs.vnops.rename Number of VOP_RENAME calls per filesystem +VOP_RENAME is used to implement rename(2) system call +@kernel.fs.vnops.rmdir Number of VOP_RMDIR calls per filesystem +VOP_RMDIR is used to implement rmdir(2) system call +@kernel.fs.vnops.rwlock Number of VOP_RWLOCK calls per filesystem +VOP_RWLOCK and VOP_RWUNLOCK are used to protect access to vnode data. +@kernel.fs.vnops.rwunlock Number of VOP_RWUNLOCK calls per filesystem +VOP_RWLOCK and VOP_RWUNLOCK are used to protect access to vnode data. +@kernel.fs.vnops.seek Number of VOP_SEEK calls per filesystem +VOP_SEEK is used by lseek(2). Because vnodes can be shared across multiple +instances of vfile VOP_SEEK does not usually change the position of the +file pointer, it instead used to verify the offset before it is changed. +@kernel.fs.vnops.setattr Number of VOP_SETATTR calls per filesystem +VOP_SETATTR is used to change vnode attributes which are modified by system +calls like chmod(2), chown(2), utimes(2) etc. +@kernel.fs.vnops.setfl Number of VOP_SETFL calls per filesystem +VOP_SETFL is used to implement fcntl(2) F_SETFL option. +Currently only sockfs pseudo filesystem is implementing this vnode operation. +@kernel.fs.vnops.setsecattr Number of VOP_SETSECATTR calls per filesystem +VOP_SETSECATTR is used to change ACL entries +@kernel.fs.vnops.shrlock Number of VOP_SHRLOCK calls per filesystem +VOP_SHRLOCK is usually used to implement CIFS and NLMv3 shared reservations. +@kernel.fs.vnops.space Number of VOP_SPACE calls per filesystem +VOP_SPACE is used to provide optimized support for growing and shrinking the files. +F_FREESP option of fcntl(2) is using this vnode operation to implment ftruncate(3c) +function. +@kernel.fs.vnops.symlink Number of VOP_SYMLINK calls per filesystem +VOP_SYMLINK is used to create symbolic links. +@kernel.fs.vnops.vnevent Number of VOP_VNEVENT calls per filesystem +VIP_VNEVENT is used to check if a filesystem support vnode event +notifications for operations which change the names of the files. +@kernel.fs.vnops.write Number of VOP_WRITE calls per filesystem +VOP_WRITE is used to implement write(2) system call +@kernel.fs.read_bytes Number of bytes read from a specific filesystem +@kernel.fs.readdir_bytes Number of bytes containting directory entires read from a specific filesystem +@kernel.fs.write_bytes Number of bytes written to a specific filesystem + +@kernel.fstype.vnops.access Number of VOP_ACCESS calls for all filesystems of a given type +VOP_ACCESS is used by access(2) system call. +@kernel.fstype.vnops.addmap Number of VOP_ADDMAP calls for all filesystems of a given type +VOP_ADDMAP is used to manage reference counting of the vnode used by +mmap(2) operations. +@kernel.fstype.vnops.close Number of VOP_CLOSE calls for the specific filesystem +VOP_CLOSE is called every time a close(2) system call is called +@kernel.fstype.vnops.cmp Number of VOP_CMP calls for the specific filesystem +VOP_CMP is used to check if two vnodes are "equal" to each other, i.e. +both refer to the same filesystem object. +@kernel.fstype.vnops.create Number of VOP_CREATE calls for all filesystems of a given type +VOP_CREATE is used to create regular files and device or FIFO nodes. +@kernel.fstype.vnops.delmap Number of VOP_DELMAP was called +VOP_DELMAP is used to destroy a previously created memory-mapped region +of a file. +@kernel.fstype.vnops.dispose Number ot VOP_DISPOSE calls for all filesystems of a given type +VOP_DISPOSE is used to dispose(free or invalidate) of a page associated +with a file. +@kernel.fstype.vnops.dump Number of VOP_DUMP calls for all filesystems of a given type +VOP_DUMP is used to transfer data from the frozen kernel directly +to the dump device +@kernel.fstype.vnops.dumpctl Number of VOP_DUMPCTL calls for all filesystems of a given type +VOP_DUMPCTL sets up context used by VOP_DUMP call. It is used to +allocate, free or search for data blocks on the dump device. +@kernel.fstype.vnops.fid Number of VOP_FID calls for all filesystems of a given type +VOP_FID is used to get file identifier which can be used instead of the +file name in some operations. NFS server is one known user of this vnode +operation. +@kernel.fstype.vnops.frlock Number of time VOP_FRLOCK calls for all filesystems of a given type +VOP_FRLOCK is used to implement file record locking used by flock(2) +@kernel.fstype.vnops.fsync Number of VOP_FSYNC calls for all filesystems of a given type +VOP_FSYNC is used to implement fsync(2) system call which flushes +data for a specific file to disk. +@kernel.fstype.vnops.getattr Number of VOP_GETATTR calls for all filesystems of a given type +VOP_GETATTR is used to extract vnode attributes. It use used as part of +many system calls which manipulate file attributes, e.g. chmod(2), stat(2), +utimes(2) etc. +@kernel.fstype.vnops.getpage Number of VOP_GETPAGE calls for all filesystems of a given type +VOP_GETPAGE is used to allocate pages (could be several at a time) to cover +a region in a file. +@kernel.fstype.vnops.getsecattr Number of VOP_GETSECATTR calls for all filesystems of a given type +VOP_GETSECATTR used to extract ACL entires associated with a file. +@kernel.fstype.vnops.inactive Number of VOP_INACTIVE calls for all filesystems of a given type +VOP_INACTIVE is used to destroy vnode before it is removed from the +cache or reused. +@kernel.fstype.vnops.ioctl Number of VOP_IOCTL calls for all filesystems of a given type +VOP_IOCTL is used to implement ioctl(2) system call. +@kernel.fstype.vnops.link Number of VOP_LINK calls for all filesystems of a given type +VOP_LINK is used to implement support for hard links +@kernel.fstype.vnops.lookup Number of VOP_LOOKUP calls for all filesystems of a given type +VOP_LOOKUP is used to translate filename to vnode. +@kernel.fstype.vnops.map Number of VOP_MAP calls for all filesystems of a given type +VOP_MAP is used to create a new memory-mapped region of a file +@kernel.fstype.vnops.mkdir Number of VOP_MKDIR calls for all filesystems of a given type +VOP_MKDIR is used to create directories +@kernel.fstype.vnops.open Number of VOP_OPEN calls for all filesystems of a given type +VOP_OPEN is called every time open(2) system call is called. +@kernel.fstype.vnops.pageio Number of VOP_PAGEIO calls for all filesystems of a given type +VOP_PAGEIO is similar to VOP_GETPAGE and VOP_PUTPAGE and can be used when +either of the other two are less efficient, e.g. in the case when pages +will be reused after the IO is done. +@kernel.fstype.vnops.pathconf Number of VOP_PATHCONF calls for all filesystems of a given type +VOP_PATHCONF is used to obtain information about filesystem's parameters +reported by pathconf(2) system call +@kernel.fstype.vnops.poll Number of VOP_POLL calls for all filesystems of a given type +VOP_POLL is used to implement pool(2) system call +@kernel.fstype.vnops.putpage Number of VOP_PUTPAGE calls for all filesystems of a given type +VOP_PUTPAGE is used to release pages which have been used to hold +data from a file +@kernel.fstype.vnops.read Number of VOP_READ calls for all filesystems of a given type +VOP_READ is used to implement read(2) system call +@kernel.fstype.vnops.readdir Number of VOP_READDIR calls for all filesystems of a given type +VOP_READDIR is used to read directory entries +@kernel.fstype.vnops.readlink Number of VOP_READLINK calls for all filesystems of a given type +VOP_READLINK is used to read the information about the target of the symbolic +link +@kernel.fstype.vnops.realvp Number of VOP_REALVP calls for all filesystems of a given type +VOP_REALVP is used to traverse stacking filesystems and extract information +about the vnode which refers to the "real" filesystem object. +@kernel.fstype.vnops.remove Number of VOP_REMOVE calls for all filesystems of a given type +VOP_REMOVE is used to remove entires from a directory. +@kernel.fstype.vnops.rename Number of VOP_RENAME calls for all filesystems of a given type +VOP_RENAME is used to implement rename(2) system call +@kernel.fstype.vnops.rmdir Number of VOP_RMDIR calls for all filesystems of a given type +VOP_RMDIR is used to implement rmdir(2) system call +@kernel.fstype.vnops.rwlock Number of VOP_RWLOCK calls for all filesystems of a given type +VOP_RWLOCK and VOP_RWUNLOCK are used to protect access to vnode data. +@kernel.fstype.vnops.rwunlock Number of VOP_RWUNLOCK calls for all filesystems of a given type +VOP_RWLOCK and VOP_RWUNLOCK are used to protect access to vnode data. +@kernel.fstype.vnops.seek Number of VOP_SEEK calls for all filesystems of a given type +VOP_SEEK is used by lseek(2). Because vnodes can be shared across multiple +instances of vfile VOP_SEEK does not usually change the position of the +file pointer, it instead used to verify the offset before it is changed. +@kernel.fstype.vnops.setattr Number of VOP_SETATTR calls for all filesystems of a given type +VOP_SETATTR is used to change vnode attributes which are modified by system +calls like chmod(2), chown(2), utimes(2) etc. +@kernel.fstype.vnops.setfl Number of VOP_SETFL calls for all filesystems of a given type +VOP_SETFL is used to implement fcntl(2) F_SETFL option. +Currently only sockfs pseudo filesystem is implementing this vnode operation. +@kernel.fstype.vnops.setsecattr Number of VOP_SETSECATTR calls for all filesystems of a given type +VOP_SETSECATTR is used to change ACL entries +@kernel.fstype.vnops.shrlock Number of VOP_SHRLOCK calls for all filesystems of a given type +VOP_SHRLOCK is usually used to implement CIFS and NLMv3 shared reservations. +@kernel.fstype.vnops.space Number of VOP_SPACE calls for all filesystems of a given type +VOP_SPACE is used to provide optimized support for growing and shrinking the files. +F_FREESP option of fcntl(2) is using this vnode operation to implment ftruncate(3c) +function. +@kernel.fstype.vnops.symlink Number of VOP_SYMLINK calls for all filesystems of a given type +VOP_SYMLINK is used to create symbolic links. +@kernel.fstype.vnops.vnevent Number of VOP_VNEVENT calls for all filesystems of a given type +VIP_VNEVENT is used to check if a filesystem support vnode event +notifications for operations which change the names of the files. +@kernel.fstype.vnops.write Number of VOP_WRITE calls for all filesystems of a given type +VOP_WRITE is used to implement write(2) system call +@kernel.fstype.read_bytes Bytes read from all filesystems of a given type +@kernel.fstype.readdir_bytes Bytes read for directory entries from all filesystems of a given type +@kernel.fstype.write_bytes Bytes written to all filesystems of a given type + +@hinv.disk.devlink Disk name in the descriptive format +Solaris uses symbolic links under /dev to provide access to device nodes via +"descriptive" names like /dev/dsk/cXtYdZsN. This metrics provides a +translation from a "descriptive" name to instances in the disk instance +domain. + +The name is always the name of the first minor device for a particular disk +and includes the slice information. + +NOTE! Fetching this metric is expensive - several system calls are made + to fetch each instance. diff --git a/src/pmdas/solaris/kvm.c b/src/pmdas/solaris/kvm.c new file mode 100644 index 0000000..5aa674a --- /dev/null +++ b/src/pmdas/solaris/kvm.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Max Matveev. All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include "common.h" + + +static kvm_t *kvm; +struct nlist kvm_names[] = { + {.n_name = "fsf_total"}, + {.n_name = NULL} +}; + +fsf_stat_t s = {0}; +static int fresh; + +void +kvm_init(int ignore) +{ + kvm = kvm_open(NULL, NULL, NULL, O_RDONLY, "pmdasolaris"); + if (kvm && kvm_nlist(kvm, kvm_names)) + fprintf(stderr, "Cannot extract addresses\n"); +} + +void +kvm_refresh(void) +{ + fresh = kvm && + (kvm_kread(kvm, kvm_names[0].n_value, &s, sizeof(s)) == sizeof(s)); +} + +int +kvm_fetch(pmdaMetric *pm, int inst, pmAtomValue *v) +{ + metricdesc_t *md = pm->m_user; + char *p = (char *)&s; + + if (!fresh) + return 0; + + memcpy(&v->ull, p + md->md_offset, sizeof(v->ull)); + return 1; +} diff --git a/src/pmdas/solaris/netlink.c b/src/pmdas/solaris/netlink.c new file mode 100644 index 0000000..d3b61bf --- /dev/null +++ b/src/pmdas/solaris/netlink.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Extract per-link network information via kstat. + * + * Link stats are in the sections called "link" and the stat name + * is the same as the link names */ + +#include +#include "common.h" + +int +netlink_fetch(pmdaMetric *pm, int inst, pmAtomValue *av) +{ + char *lname; + metricdesc_t *md = pm->m_user; + kstat_t *k; + char *stat = (char *)md->md_offset; + + if (pmdaCacheLookup(indomtab[NETLINK_INDOM].it_indom, inst, &lname, + (void **)&k) != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + if (k) { + kstat_named_t *kn = kstat_data_lookup(k, stat); + + if (kn == NULL) { + fprintf(stderr, "No kstat called %s for %s\n", stat, lname); + return 0; + } + + switch (pm->m_desc.type) { + case PM_TYPE_32: + if (kn->data_type == KSTAT_DATA_INT32) { + av->l = kn->value.i32; + return 1; + } + break; + case PM_TYPE_U32: + if (kn->data_type == KSTAT_DATA_UINT32) { + av->ul = kn->value.ui32; + return 1; + } + break; + case PM_TYPE_64: + if (kn->data_type == KSTAT_DATA_INT64) { + av->ll = kn->value.i64; + return 1; + } + break; + case PM_TYPE_U64: + if (kn->data_type == KSTAT_DATA_UINT64) { + av->ull = kn->value.ui64; + return 1; + } + break; + } + } + + return 0; +} + +void +netlink_update_stats(int fetch) +{ + kstat_t *k; + kstat_ctl_t *kc; + pmInDom indom = indomtab[NETLINK_INDOM].it_indom; + + if ((kc = kstat_ctl_update()) == NULL) + return; + + for (k = kc->kc_chain; k != NULL; k = k->ks_next) { + if (strcmp(k->ks_module, "link") == 0) { + int rv; + kstat_t *cached; + + if (pmdaCacheLookupName(indom, k->ks_name, &rv, + (void **)&cached) != PMDA_CACHE_ACTIVE) { + rv = pmdaCacheStore(indom, PMDA_CACHE_ADD, k->ks_name, k); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot create instance for " + "network link '%s': %s\n", + k->ks_name, pmErrStr(rv)); + continue; + } + } + + if (fetch) + kstat_read(kc, k, NULL); + } + } +} + +void +netlink_refresh(void) +{ + pmdaCacheOp(indomtab[NETLINK_INDOM].it_indom, PMDA_CACHE_INACTIVE); + netlink_update_stats(1); + pmdaCacheOp(indomtab[NETLINK_INDOM].it_indom, PMDA_CACHE_SAVE); +} + +void +netlink_init(int first) +{ + pmdaCacheOp(indomtab[NETLINK_INDOM].it_indom, PMDA_CACHE_LOAD); + netlink_update_stats(0); + pmdaCacheOp(indomtab[NETLINK_INDOM].it_indom, PMDA_CACHE_SAVE); +} diff --git a/src/pmdas/solaris/netmib2.c b/src/pmdas/solaris/netmib2.c new file mode 100644 index 0000000..aab417a --- /dev/null +++ b/src/pmdas/solaris/netmib2.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2009 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* Extract network-related information from the kernel using MIB2 + * interfaces. MIB2 structures are described by RFC 4113, 4293, + * 4001. IPv6 specific MIB structures are described in RFC 2465, 2466. + */ + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "netmib2.h" + +static int afd = -1; +static int data_valid; +static int netif_added; + +nm2_udp_stats_t nm2_udp; + +static nm2_netif_stats_t * +netif_cache_inst(const char *ifname) +{ + pmInDom indom = indomtab[NETIF_INDOM].it_indom; + nm2_netif_stats_t *ist; + int rv; + + if (pmdaCacheLookupName(indom, ifname, &rv, + (void **)&ist) != PMDA_CACHE_ACTIVE) { + ist = malloc(sizeof(*ist)); + if (ist == NULL) { + __pmNotifyErr(LOG_WARNING, + "Out of memory for stats on network interface '%s'\n", + ifname); + return NULL; + } + + rv = pmdaCacheStore(indom, PMDA_CACHE_ADD, ifname, ist); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot create instance for '%s': %s\n", + ifname, pmErrStr(rv)); + free(ist); + return NULL; + } + netif_added++; + } + + return ist; +} + +static void +ipv4_stats (const void *data, int sz) +{ + const mib2_ipAddrEntry_t *ipa = data; + + while (sz > 0) { + nm2_netif_stats_t *ist = netif_cache_inst(ipa->ipAdEntIfIndex.o_bytes); + + if (ist) { + ist->mtu = ipa->ipAdEntInfo.ae_mtu; + /* We get byte count and other stuff from Traffic stats */ + } + sz -= sizeof(*ipa); + ipa++; + } +} + +static void +ipv4_ifstats(const void *data, int sz) +{ + const mib2_ipIfStatsEntry_t *ips = data; + + nm2_udp.noports = 0; + nm2_udp.overflows = 0; + + while (sz > 0) { + /* index 0 is a pseudo-interface */ + if (ips->ipIfStatsIfIndex) { + nm2_netif_stats_t *ist; + char name[64]; + + if ((if_indextoname(ips->ipIfStatsIfIndex, name) != NULL) && + ((ist = netif_cache_inst(name)) != NULL)) { + + ist->ibytes = ips->ipIfStatsHCInOctets; + ist->obytes = ips->ipIfStatsHCOutOctets; + ist->ipackets = ips->ipIfStatsHCInReceives; + ist->opackets = ips->ipIfStatsHCOutTransmits; + ist->imcast = ips->ipIfStatsHCInMcastPkts; + ist->omcast = ips->ipIfStatsHCOutMcastPkts; + ist->ibcast = ips->ipIfStatsHCInBcastPkts; + ist->obcast = ips->ipIfStatsHCOutBcastPkts; + ist->delivered = ips->ipIfStatsHCInDelivers; + ist->idrops = ips->ipIfStatsInDiscards; + ist->odrops = ips->ipIfStatsOutDiscards; + ist->ierrors = + + (uint64_t)ips->ipIfStatsInHdrErrors + + ips->ipIfStatsInTooBigErrors + + ips->ipIfStatsInNoRoutes + + ips->ipIfStatsInAddrErrors + + ips->ipIfStatsInUnknownProtos + + ips->ipIfStatsInTruncatedPkts; + + ist->oerrors = ips->ipIfStatsOutFragFails; + } + } + + nm2_udp.noports += ips->udpNoPorts; + nm2_udp.overflows += ips->udpInOverflows; + + sz -= sizeof(*ips); + ips++; + } +} + +void +netmib2_refresh(void) +{ + struct strbuf ctrl; + struct opthdr *oh; + uint64_t buf[64]; /* Arbitrary size, just large enough to fit req + opthdr */ + struct T_optmgmt_req *omreq = (struct T_optmgmt_req *)buf; + struct T_optmgmt_ack *omack = (struct T_optmgmt_ack *)buf; + + omreq->PRIM_type = T_SVR4_OPTMGMT_REQ; + omreq->OPT_offset = sizeof (*omreq); + omreq->OPT_length = sizeof (*oh); + omreq->MGMT_flags = T_CURRENT; + + oh = (struct opthdr *)(omreq + 1); + oh->level = /*EXPER_IP_AND_TESTHIDDEN*/MIB2_IP; + oh->name = 0; + oh->len = 0; + + ctrl.buf = (char *)buf; + ctrl.len = omreq->OPT_length + omreq->OPT_offset; + + data_valid = 0; + + if (putmsg(afd, &ctrl, NULL, 0) == -1) { + __pmNotifyErr(LOG_ERR, "Failed to push message down stream: %s\n", + osstrerror()); + return; + } + + oh = (struct opthdr *)(omack + 1); + ctrl.maxlen = sizeof(buf); + + netif_added = 0; + + for (;;) { + int flags = 0; + struct strbuf data; + int rv; + + rv = getmsg(afd, &ctrl, NULL, &flags); + if (rv < 0) { + __pmNotifyErr(LOG_ERR, "netmib2: failed to get a response: %s\n", + osstrerror()); + break; + } + + if ((rv == 0) && (ctrl.len >= sizeof(*omack)) && + (omack->PRIM_type == T_OPTMGMT_ACK) && + (omack->MGMT_flags == T_SUCCESS) && (oh->len == 0)) { + data_valid = 1; + break; + } + + if ((rv != MOREDATA) || (ctrl.len < sizeof(*omack)) || + (omack->PRIM_type != T_OPTMGMT_ACK) || + (omack->MGMT_flags != T_SUCCESS)) { + __pmNotifyErr(LOG_ERR, "netmib2: Unexpected message received\n"); + break; + } + + memset(&data, 0, sizeof(data)); + data.buf = malloc(oh->len); + if (data.buf == NULL) { + __pmNotifyErr(LOG_ERR, "netmib2: Out of memory\n"); + break; + } + + data.maxlen = oh->len; + flags = 0; + + rv = getmsg(afd, NULL, &data, &flags); + if (rv) { + __pmNotifyErr(LOG_ERR, + "net2mib: Failed to get additional data: %s\n", + osstrerror()); + break; + } + + switch (oh->level) { + case MIB2_IP: + switch(oh->name) { + case 0: /* Overall statistic */ + break; + + case MIB2_IP_ADDR: + ipv4_stats(data.buf, data.len); + break; + + case MIB2_IP_TRAFFIC_STATS: + ipv4_ifstats(data.buf, data.len); + break; + } + break; + + case MIB2_IP6: + break; + + case MIB2_UDP: + if (oh->name == 0) { + mib2_udp_t *m2u = (mib2_udp_t *)data.buf; + +#ifdef EXPER_IP_AND_TESTHIDDEN + nm2_udp.ipackets = m2u->udpHCInDatagrams; + nm2_udp.opackets = m2u->udpHCOutDatagrams; +#else + nm2_udp.ipackets = m2u->udpInDatagrams; + nm2_udp.opackets = m2u->udpOutDatagrams; +#endif + nm2_udp.ierrors = m2u->udpInErrors; + nm2_udp.oerrors = m2u->udpOutErrors; + } + break; + + case MIB2_TCP: + break; + } + + free(data.buf); + } + + if (netif_added) { + pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} + +int +netmib2_fetch(pmdaMetric *pm, int inst, pmAtomValue *av) +{ + char *fsname; + metricdesc_t *md = pm->m_user; + char *ist; + + if (pm->m_desc.indom == PM_INDOM_NULL) { + switch (pm->m_desc.type) { + case PM_TYPE_U32: + av->ul = *(uint32_t *)md->md_offset; + return 1; + + case PM_TYPE_U64: + av->ull = *(uint64_t *)md->md_offset; + return 1; + } + + return PM_ERR_APPVERSION; + } + + if (pmdaCacheLookup(indomtab[NETIF_INDOM].it_indom, inst, &fsname, + (void **)&ist) != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + if (ist) { + switch (pm->m_desc.type) { + case PM_TYPE_U32: + av->ul = *(uint32_t *)(ist + md->md_offset); + return 1; + + case PM_TYPE_U64: + av->ull = *(uint64_t *)(ist + md->md_offset); + return 1; + } + + return PM_ERR_APPVERSION; + } + + /* Even if we've copied the values don't admit they're good unless + * the update was problem-free. */ + return data_valid; +} + +void +netmib2_init(int first) +{ + char *mods[] = {"tcp", "udp", "icmp"}; + int i; + + if (afd >= 0) + return; + + afd = open("/dev/arp", O_RDWR); + if (afd < 0) { + __pmNotifyErr(LOG_ERR, "Cannot open /dev/arp: %s\n", osstrerror()); + return; + } + + for (i = 0; i < 3; i++ ) { + if (ioctl(afd, I_PUSH, mods[i]) < 0) { + __pmNotifyErr(LOG_ERR, "Cannot push %s into /dev/arp: %s\n", + mods[i], osstrerror()); + close(afd); + afd = -1; + return; + } + } + + pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_LOAD); + netmib2_refresh(); +} diff --git a/src/pmdas/solaris/netmib2.h b/src/pmdas/solaris/netmib2.h new file mode 100644 index 0000000..e506c28 --- /dev/null +++ b/src/pmdas/solaris/netmib2.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __PMDA_SOLARIS_NETMIB2_H +#define __PMDA_SOLARIS_NETMIB2_H + +typedef struct nm2_udp_stats { + uint64_t ipackets; + uint64_t opackets; + int32_t ierrors; + int32_t oerrors; + uint32_t noports; + uint32_t overflows; +} nm2_udp_stats_t; + +extern nm2_udp_stats_t nm2_udp; + +typedef struct nm2_netif_stats { + uint64_t ipackets; + uint64_t opackets; + uint64_t ibytes; + uint64_t obytes; + uint64_t delivered; + uint64_t imcast; + uint64_t omcast; + uint64_t ibcast; + uint64_t obcast; + uint64_t ierrors; + uint64_t oerrors; + int32_t idrops; + int32_t odrops; + int mtu; +} nm2_netif_stats_t; + +void netmib2_init(int); +void netmib2_refresh(void); +int netmib2_fetch(pmdaMetric *, int, pmAtomValue *); + +#endif diff --git a/src/pmdas/solaris/pmns.disk b/src/pmdas/solaris/pmns.disk new file mode 100644 index 0000000..1875a25 --- /dev/null +++ b/src/pmdas/solaris/pmns.disk @@ -0,0 +1,58 @@ +disk { + all + dev +} + +disk.all { + read SOLARIS:SCLR_DISK:0 + write SOLARIS:SCLR_DISK:1 + total SOLARIS:SCLR_DISK:2 + read_bytes SOLARIS:SCLR_DISK:3 + write_bytes SOLARIS:SCLR_DISK:4 + total_bytes SOLARIS:SCLR_DISK:5 + wait + run +} + +disk.all.wait { + time SOLARIS:SCLR_DISK:6 + count SOLARIS:SCLR_DISK:7 +} + +disk.all.run { + time SOLARIS:SCLR_DISK:8 + count SOLARIS:SCLR_DISK:9 +} + +disk.dev { + read SOLARIS:SCLR_DISK:10 + write SOLARIS:SCLR_DISK:11 + total SOLARIS:SCLR_DISK:12 + read_bytes SOLARIS:SCLR_DISK:13 + write_bytes SOLARIS:SCLR_DISK:14 + total_bytes SOLARIS:SCLR_DISK:15 + wait + run + errors +} + +disk.dev.wait { + time SOLARIS:SCLR_DISK:16 + count SOLARIS:SCLR_DISK:17 +} +disk.dev.run { + time SOLARIS:SCLR_DISK:18 + count SOLARIS:SCLR_DISK:19 +} + +disk.dev.errors { + soft SOLARIS:SCLR_DISK:21 + hard SOLARIS:SCLR_DISK:22 + transport SOLARIS:SCLR_DISK:23 + media SOLARIS:SCLR_DISK:24 + recoverable SOLARIS:SCLR_DISK:25 + notready SOLARIS:SCLR_DISK:26 + nodevice SOLARIS:SCLR_DISK:27 + badrequest SOLARIS:SCLR_DISK:28 + pfa SOLARIS:SCLR_DISK:29 +} diff --git a/src/pmdas/solaris/pmns.hinv b/src/pmdas/solaris/pmns.hinv new file mode 100644 index 0000000..964e166 --- /dev/null +++ b/src/pmdas/solaris/pmns.hinv @@ -0,0 +1,34 @@ +hinv { + ncpu SOLARIS:SCLR_SYSINFO:56 + ndisk SOLARIS:SCLR_DISK:20 + nfilesys SOLARIS:SCLR_FILESYS:1023 + pagesize SOLARIS:SCLR_SYSINFO:108 + physmem SOLARIS:SCLR_SYSINFO:109 + cpu + disk +} + +hinv.cpu { + maxclock SOLARIS:SCLR_SYSINFO:147 + clock SOLARIS:SCLR_SYSINFO:148 + brand SOLARIS:SCLR_SYSINFO:149 + frequencies SOLARIS:SCLR_SYSINFO:150 + implementation SOLARIS:SCLR_SYSINFO:151 + chip_id SOLARIS:SCLR_SYSINFO:152 + clog_id SOLARIS:SCLR_SYSINFO:153 + core_id SOLARIS:SCLR_SYSINFO:154 + pkg_core_id SOLARIS:SCLR_SYSINFO:155 + cstate SOLARIS:SCLR_SYSINFO:156 + maxcstates SOLARIS:SCLR_SYSINFO:157 + ncores SOLARIS:SCLR_SYSINFO:158 + ncpus SOLARIS:SCLR_SYSINFO:159 +} + +hinv.disk { + vendor SOLARIS:SCLR_DISK:30 + product SOLARIS:SCLR_DISK:31 + revision SOLARIS:SCLR_DISK:32 + serial SOLARIS:SCLR_DISK:33 + capacity SOLARIS:SCLR_DISK:34 + devlink SOLARIS:SCLR_DISK:35 +} diff --git a/src/pmdas/solaris/pmns.kernel b/src/pmdas/solaris/pmns.kernel new file mode 100644 index 0000000..d6722ae --- /dev/null +++ b/src/pmdas/solaris/pmns.kernel @@ -0,0 +1,200 @@ +kernel { + all + percpu + fsflush + fs + fstype +} + +kernel.all { + cpu + io + trap SOLARIS:SCLR_SYSINFO:32 + pswitch SOLARIS:SCLR_SYSINFO:23 + syscall SOLARIS:SCLR_SYSINFO:22 + sysexec SOLARIS:SCLR_SYSINFO:33 + sysfork SOLARIS:SCLR_SYSINFO:34 + sysvfork SOLARIS:SCLR_SYSINFO:35 + sysread SOLARIS:SCLR_SYSINFO:36 + syswrite SOLARIS:SCLR_SYSINFO:37 + load SOLARIS:SCLR_SYSINFO:135 +} + +kernel.all.cpu { + idle SOLARIS:SCLR_SYSINFO:0 + user SOLARIS:SCLR_SYSINFO:1 + sys SOLARIS:SCLR_SYSINFO:2 + wait +} + +kernel.all.cpu.wait { + total SOLARIS:SCLR_SYSINFO:3 + io SOLARIS:SCLR_SYSINFO:8 + pio SOLARIS:SCLR_SYSINFO:9 + swap SOLARIS:SCLR_SYSINFO:10 +} + +kernel.all.io { + bread SOLARIS:SCLR_SYSINFO:14 + bwrite SOLARIS:SCLR_SYSINFO:15 + lread SOLARIS:SCLR_SYSINFO:16 + lwrite SOLARIS:SCLR_SYSINFO:17 + phread SOLARIS:SCLR_SYSINFO:26 + phwrite SOLARIS:SCLR_SYSINFO:27 + intr SOLARIS:SCLR_SYSINFO:28 +} + +kernel.percpu { + cpu + io + trap SOLARIS:SCLR_SYSINFO:38 + pswitch SOLARIS:SCLR_SYSINFO:25 + syscall SOLARIS:SCLR_SYSINFO:24 + sysexec SOLARIS:SCLR_SYSINFO:39 + sysfork SOLARIS:SCLR_SYSINFO:40 + sysvfork SOLARIS:SCLR_SYSINFO:41 + sysread SOLARIS:SCLR_SYSINFO:42 + syswrite SOLARIS:SCLR_SYSINFO:43 +} + +kernel.percpu.cpu { + idle SOLARIS:SCLR_SYSINFO:4 + user SOLARIS:SCLR_SYSINFO:5 + sys SOLARIS:SCLR_SYSINFO:6 + wait +} + +kernel.percpu.cpu.wait { + total SOLARIS:SCLR_SYSINFO:7 + io SOLARIS:SCLR_SYSINFO:11 + pio SOLARIS:SCLR_SYSINFO:12 + swap SOLARIS:SCLR_SYSINFO:13 +} + +kernel.percpu.io { + bread SOLARIS:SCLR_SYSINFO:18 + bwrite SOLARIS:SCLR_SYSINFO:19 + lread SOLARIS:SCLR_SYSINFO:20 + lwrite SOLARIS:SCLR_SYSINFO:21 + phread SOLARIS:SCLR_SYSINFO:29 + phwrite SOLARIS:SCLR_SYSINFO:30 + intr SOLARIS:SCLR_SYSINFO:31 +} + +kernel.fsflush { + scanned SOLARIS:SCLR_FSFLUSH:0 + examined SOLARIS:SCLR_FSFLUSH:1 + locked SOLARIS:SCLR_FSFLUSH:2 + modified SOLARIS:SCLR_FSFLUSH:3 + coalesced SOLARIS:SCLR_FSFLUSH:4 + released SOLARIS:SCLR_FSFLUSH:5 + time SOLARIS:SCLR_FSFLUSH:6 +} + +kernel.fs { + vnops + read_bytes SOLARIS:SCLR_FILESYS:0 + readdir_bytes SOLARIS:SCLR_FILESYS:1 + write_bytes SOLARIS:SCLR_FILESYS:2 +} + +kernel.fs.vnops { + access SOLARIS:SCLR_FILESYS:3 + addmap SOLARIS:SCLR_FILESYS:4 + close SOLARIS:SCLR_FILESYS:5 + cmp SOLARIS:SCLR_FILESYS:6 + create SOLARIS:SCLR_FILESYS:7 + delmap SOLARIS:SCLR_FILESYS:8 + dispose SOLARIS:SCLR_FILESYS:9 + dump SOLARIS:SCLR_FILESYS:10 + dumpctl SOLARIS:SCLR_FILESYS:11 + fid SOLARIS:SCLR_FILESYS:12 + frlock SOLARIS:SCLR_FILESYS:13 + fsync SOLARIS:SCLR_FILESYS:14 + getattr SOLARIS:SCLR_FILESYS:15 + getpage SOLARIS:SCLR_FILESYS:16 + getsecattr SOLARIS:SCLR_FILESYS:17 + inactive SOLARIS:SCLR_FILESYS:18 + ioctl SOLARIS:SCLR_FILESYS:19 + link SOLARIS:SCLR_FILESYS:20 + lookup SOLARIS:SCLR_FILESYS:21 + map SOLARIS:SCLR_FILESYS:22 + mkdir SOLARIS:SCLR_FILESYS:23 + open SOLARIS:SCLR_FILESYS:24 + pageio SOLARIS:SCLR_FILESYS:25 + pathconf SOLARIS:SCLR_FILESYS:26 + poll SOLARIS:SCLR_FILESYS:27 + putpage SOLARIS:SCLR_FILESYS:28 + read SOLARIS:SCLR_FILESYS:29 + readdir SOLARIS:SCLR_FILESYS:30 + readlink SOLARIS:SCLR_FILESYS:31 + realvp SOLARIS:SCLR_FILESYS:32 + remove SOLARIS:SCLR_FILESYS:33 + rename SOLARIS:SCLR_FILESYS:34 + rmdir SOLARIS:SCLR_FILESYS:35 + rwlock SOLARIS:SCLR_FILESYS:36 + rwunlock SOLARIS:SCLR_FILESYS:37 + seek SOLARIS:SCLR_FILESYS:38 + setattr SOLARIS:SCLR_FILESYS:39 + setfl SOLARIS:SCLR_FILESYS:40 + setsecattr SOLARIS:SCLR_FILESYS:41 + shrlock SOLARIS:SCLR_FILESYS:42 + space SOLARIS:SCLR_FILESYS:43 + symlink SOLARIS:SCLR_FILESYS:44 + vnevent SOLARIS:SCLR_FILESYS:45 + write SOLARIS:SCLR_FILESYS:46 +} + +kernel.fstype { + vnops + read_bytes SOLARIS:SCLR_FILESYS:47 + readdir_bytes SOLARIS:SCLR_FILESYS:48 + write_bytes SOLARIS:SCLR_FILESYS:49 +} + +kernel.fstype.vnops { + access SOLARIS:SCLR_FILESYS:50 + addmap SOLARIS:SCLR_FILESYS:51 + close SOLARIS:SCLR_FILESYS:52 + cmp SOLARIS:SCLR_FILESYS:53 + create SOLARIS:SCLR_FILESYS:54 + delmap SOLARIS:SCLR_FILESYS:55 + dispose SOLARIS:SCLR_FILESYS:56 + dump SOLARIS:SCLR_FILESYS:57 + dumpctl SOLARIS:SCLR_FILESYS:58 + fid SOLARIS:SCLR_FILESYS:59 + frlock SOLARIS:SCLR_FILESYS:60 + fsync SOLARIS:SCLR_FILESYS:61 + getattr SOLARIS:SCLR_FILESYS:62 + getpage SOLARIS:SCLR_FILESYS:63 + getsecattr SOLARIS:SCLR_FILESYS:64 + inactive SOLARIS:SCLR_FILESYS:65 + ioctl SOLARIS:SCLR_FILESYS:66 + link SOLARIS:SCLR_FILESYS:67 + lookup SOLARIS:SCLR_FILESYS:68 + map SOLARIS:SCLR_FILESYS:69 + mkdir SOLARIS:SCLR_FILESYS:70 + open SOLARIS:SCLR_FILESYS:71 + pageio SOLARIS:SCLR_FILESYS:72 + pathconf SOLARIS:SCLR_FILESYS:73 + poll SOLARIS:SCLR_FILESYS:74 + putpage SOLARIS:SCLR_FILESYS:75 + read SOLARIS:SCLR_FILESYS:76 + readdir SOLARIS:SCLR_FILESYS:77 + readlink SOLARIS:SCLR_FILESYS:78 + realvp SOLARIS:SCLR_FILESYS:79 + remove SOLARIS:SCLR_FILESYS:80 + rename SOLARIS:SCLR_FILESYS:81 + rmdir SOLARIS:SCLR_FILESYS:82 + rwlock SOLARIS:SCLR_FILESYS:83 + rwunlock SOLARIS:SCLR_FILESYS:84 + seek SOLARIS:SCLR_FILESYS:85 + setattr SOLARIS:SCLR_FILESYS:86 + setfl SOLARIS:SCLR_FILESYS:87 + setsecattr SOLARIS:SCLR_FILESYS:88 + shrlock SOLARIS:SCLR_FILESYS:89 + space SOLARIS:SCLR_FILESYS:90 + symlink SOLARIS:SCLR_FILESYS:91 + vnevent SOLARIS:SCLR_FILESYS:92 + write SOLARIS:SCLR_FILESYS:93 +} diff --git a/src/pmdas/solaris/pmns.mem b/src/pmdas/solaris/pmns.mem new file mode 100644 index 0000000..04fb6f1 --- /dev/null +++ b/src/pmdas/solaris/pmns.mem @@ -0,0 +1,88 @@ +/* + * TODO + * + * These are the IRIX names, for reference + * mem.freemem + * mem.availsmem + * mem.availrmem + * mem.ravailrmem + * mem.bufmem + * mem.physmem + * mem.dchunkpages + * mem.pmapmem + * mem.strmem + * mem.chunkpages + * mem.dpages + * mem.emptymem + * mem.freeswap + * mem.halloc + * mem.heapmem + * mem.hfree + * mem.hovhd + * mem.hunused + * mem.zfree + * mem.zonemem + * mem.zreq + * mem.iclean + * mem.bsdnet + * mem.palloc + * mem.unmodfl + * mem.unmodsw + * mem.min_file_pages + * mem.min_free_pages + * mem.bufs.fs_metadata + * mem.bufs.fs_data + * mem.bufs.empty + * mem.bufs.inact + * mem.fault.prot.total + * mem.fault.prot.cow + * mem.fault.prot.steal + * mem.fault.addr.total + * mem.fault.addr.cache + * mem.fault.addr.demand + * mem.fault.addr.file + * mem.fault.addr.swap + * mem.tlb.flush + * mem.tlb.invalid + * mem.tlb.rfault + * mem.tlb.sync + * mem.tlb.tfault + * mem.tlb.purge + * mem.tlb.idnew + * mem.tlb.idwrap + * mem.tlb.kvmwrap + * mem.paging.reclaim + * mem.system.sptalloc + * mem.system.sptfree + * mem.system.sptclean + * mem.system.sptdirty + * mem.system.sptintrans + * mem.system.sptaged + * mem.system.sptbp + * mem.system.sptheap + * mem.system.sptzone + * mem.system.sptpt + * mem.lpage.faults + * mem.lpage.allocs + * mem.lpage.downgrade + * mem.lpage.page_splits + * mem.lpage.basesize + * mem.lpage.maxsize + * mem.lpage.maxenabled + * mem.lpage.enabled + * mem.lpage.coalesce.scans + * mem.lpage.coalesce.success + * mem.util.kernel + * mem.util.fs_ctl + * mem.util.fs_dirty + * mem.util.fs_clean + * mem.util.free + * mem.util.user + */ + +mem { + physmem SOLARIS:SCLR_SYSINFO:136 + freemem SOLARIS:SCLR_SYSINFO:137 + lotsfree SOLARIS:SCLR_SYSINFO:138 + availrmem SOLARIS:SCLR_SYSINFO:139 +} diff --git a/src/pmdas/solaris/pmns.network b/src/pmdas/solaris/pmns.network new file mode 100644 index 0000000..e9fe9f8 --- /dev/null +++ b/src/pmdas/solaris/pmns.network @@ -0,0 +1,302 @@ +/* + * TODO + * + * These are the IRIX names, for reference + * network.icmp.error + * network.icmp.oldshort + * network.icmp.oldicmp + * network.icmp.badcode + * network.icmp.tooshort + * network.icmp.checksum + * network.icmp.badlen + * network.icmp.reflect + * network.icmp.inhist.echoreply + * network.icmp.inhist.unreach + * network.icmp.inhist.sourcequench + * network.icmp.inhist.redirect + * network.icmp.inhist.echo + * network.icmp.inhist.routeradvert + * network.icmp.inhist.routersolicit + * network.icmp.inhist.timxceed + * network.icmp.inhist.paramprob + * network.icmp.inhist.tstamp + * network.icmp.inhist.tstampreply + * network.icmp.inhist.ireq + * network.icmp.inhist.ireqreply + * network.icmp.inhist.maskreq + * network.icmp.inhist.maskreply + * network.icmp.outhist.echoreply + * network.icmp.outhist.unreach + * network.icmp.outhist.sourcequench + * network.icmp.outhist.redirect + * network.icmp.outhist.echo + * network.icmp.outhist.routeradvert + * network.icmp.outhist.routersolicit + * network.icmp.outhist.timxceed + * network.icmp.outhist.paramprob + * network.icmp.outhist.tstamp + * network.icmp.outhist.tstampreply + * network.icmp.outhist.ireq + * network.icmp.outhist.ireqreply + * network.icmp.outhist.maskreq + * network.icmp.outhist.maskreply + * network.igmp.rcv_total + * network.igmp.rcv_tooshort + * network.igmp.rcv_badsum + * network.igmp.rcv_queries + * network.igmp.rcv_badqueries + * network.igmp.rcv_reports + * network.igmp.rcv_badreports + * network.igmp.rcv_ourreports + * network.igmp.snd_reports + * network.ip.badhlen + * network.ip.badlen + * network.ip.badoptions + * network.ip.badsum + * network.ip.cantforward + * network.ip.cantfrag + * network.ip.delivered + * network.ip.forward + * network.ip.fragdropped + * network.ip.fragmented + * network.ip.fragments + * network.ip.fragtimeout + * network.ip.localout + * network.ip.noproto + * network.ip.noroute + * network.ip.odropped + * network.ip.ofragments + * network.ip.reassembled + * network.ip.redirect + * network.ip.tooshort + * network.ip.toosmall + * network.ip.badvers + * network.ip.rawout + * network.ip.strictreassoverlapfrags + * network.ip.strictreassgapfrags + * network.ip.total + * network.tcp.connattempt + * network.tcp.accepts + * network.tcp.connects + * network.tcp.drops + * network.tcp.conndrops + * network.tcp.closed + * network.tcp.segstimed + * network.tcp.rttupdated + * network.tcp.delack + * network.tcp.timeoutdrop + * network.tcp.rexmttimeo + * network.tcp.persisttimeo + * network.tcp.keeptimeo + * network.tcp.keepprobe + * network.tcp.keepdrops + * network.tcp.sndtotal + * network.tcp.sndpack + * network.tcp.sndbyte + * network.tcp.sndrexmitpack + * network.tcp.sndrexmitbyte + * network.tcp.sndacks + * network.tcp.sndprobe + * network.tcp.sndurg + * network.tcp.sndwinup + * network.tcp.sndctrl + * network.tcp.sndrst + * network.tcp.rcvtotal + * network.tcp.rcvpack + * network.tcp.rcvbyte + * network.tcp.rcvbadsum + * network.tcp.rcvbadoff + * network.tcp.rcvshort + * network.tcp.rcvduppack + * network.tcp.rcvdupbyte + * network.tcp.rcvpartduppack + * network.tcp.rcvpartdupbyte + * network.tcp.rcvoopack + * network.tcp.rcvoobyte + * network.tcp.rcvpackafterwin + * network.tcp.rcvbyteafterwin + * network.tcp.rcvafterclose + * network.tcp.rcvwinprobe + * network.tcp.rcvdupack + * network.tcp.rcvacktoomuch + * network.tcp.rcvackpack + * network.tcp.rcvackbyte + * network.tcp.rcvwinupd + * network.tcp.pcbcachemiss + * network.tcp.predack + * network.tcp.preddat + * network.tcp.pawsdrop + * network.tcp.badsyn + * network.tcp.listendrop + * network.tcp.persistdrop + * network.tcp.synpurge + * network.udp.ipackets + * network.udp.hdrops + * network.udp.badsum + * network.udp.badlen + * network.udp.noport + * network.udp.noportbcast + * network.udp.fullsock + * network.udp.opackets + * network.udp.pcbcachemiss + + * network.interface.collisions + * network.interface.mtu + * network.interface.noproto + * network.interface.baudrate + * network.interface.in.errors + * network.interface.in.packets + * network.interface.in.bytes + * network.interface.in.mcasts + * network.interface.in.drops + + * network.interface.out.errors + * network.interface.out.packets + * network.interface.out.bytes + * network.interface.out.mcasts + * network.interface.out.drops + * network.interface.out.qdrops + * network.interface.out.qlength + * network.interface.out.qmax + * network.interface.total.errors + * network.interface.total.packets + * network.interface.total.bytes + * network.interface.total.mcasts + * network.interface.total.drops + * network.mbuf.alloc + * network.mbuf.typealloc + * network.mbuf.clustalloc + * network.mbuf.clustfree + * network.mbuf.failed + * network.mbuf.waited + * network.mbuf.drained + * network.mbuf.pcb.total + * network.mbuf.pcb.bytes + * network.mbuf.mcb.total + * network.mbuf.mcb.bytes + * network.mbuf.mcb.fail + * network.mcr.mfc_lookups + * network.mcr.mfc_misses + * network.mcr.upcalls + * network.mcr.no_route + * network.mcr.bad_tunnel + * network.mcr.cant_tunnel + * network.mcr.wrong_if + * network.mcr.upq_ovflw + * network.mcr.cache_cleanups + * network.mcr.drop_sel + * network.mcr.q_overflow + * network.mcr.pkt2large + * network.mcr.upq_sockfull + * network.socket.type + * network.socket.state + * network.st.connattempt + * network.st.accepts + * network.st.connects + * network.st.drops + * network.st.connfails + * network.st.closed + * network.st.txtotal + * network.st.datatxtotal + * network.st.rxtotal + * network.st.datarxtotal + * network.st.cksumbad + * network.st.oototal + * network.st.keyrejects + * network.st.txrejects + * network.st.rxrejects + * network.st.slotdrops + * network.is.in_window + * network.is.in_underflow + * network.is.in_overlap + * network.is.up_disordered + * network.is.up_ordered + * network.is.outq_full + * network.is.outq_wakeups + * network.is.outq_drains + * network.is.reorder_wakeups + * network.is.reorder_drains + * network.is.drain_underflow + * network.is.drain_loop + * network.is.drain_empty + * network.is.window_stalls + * network.is.window_flush_null + * network.is.window_seqno_fixup + * network.is.window_flush_skipped + * network.is.window_flush_nlinks + * network.is.link_quota_oflows + * network.is.link_empty_headers + * network.is.link_header_allocs + * network.is.link_soft_cksums + * network.is.link_sync_seqno + * network.is.err_bad_version + * network.is.err_input_no_link + */ + +network { + interface + link + udp +} + +network.interface { + in + out + mtu SOLARIS:SCLR_NETIF:0 +} + +network.interface.in { + errors SOLARIS:SCLR_NETIF:1 + packets SOLARIS:SCLR_NETIF:2 + bytes SOLARIS:SCLR_NETIF:3 + bcasts SOLARIS:SCLR_NETIF:4 + mcasts SOLARIS:SCLR_NETIF:5 + drops SOLARIS:SCLR_NETIF:6 + delivers SOLARIS:SCLR_NETIF:7 +} + +network.interface.out { + errors SOLARIS:SCLR_NETIF:8 + packets SOLARIS:SCLR_NETIF:9 + bytes SOLARIS:SCLR_NETIF:10 + bcasts SOLARIS:SCLR_NETIF:11 + mcasts SOLARIS:SCLR_NETIF:12 + drops SOLARIS:SCLR_NETIF:13 +} + +network.udp { + ipackets SOLARIS:SCLR_NETIF:14 + opackets SOLARIS:SCLR_NETIF:15 + ierrors SOLARIS:SCLR_NETIF:16 + oerrors SOLARIS:SCLR_NETIF:17 + noports SOLARIS:SCLR_NETIF:18 + overflows SOLARIS:SCLR_NETIF:19 +} + +network.link { + in + out + collisions SOLARIS:SCLR_NETLINK:0 + state SOLARIS:SCLR_NETLINK:1 + duplex SOLARIS:SCLR_NETLINK:2 + speed SOLARIS:SCLR_NETLINK:3 +} + +network.link.in { + errors SOLARIS:SCLR_NETLINK:4 + packets SOLARIS:SCLR_NETLINK:5 + bytes SOLARIS:SCLR_NETLINK:6 + bcasts SOLARIS:SCLR_NETLINK:7 + mcasts SOLARIS:SCLR_NETLINK:8 + nobufs SOLARIS:SCLR_NETLINK:9 +} + +network.link.out { + errors SOLARIS:SCLR_NETLINK:10 + packets SOLARIS:SCLR_NETLINK:11 + bytes SOLARIS:SCLR_NETLINK:12 + bcasts SOLARIS:SCLR_NETLINK:13 + mcasts SOLARIS:SCLR_NETLINK:14 + nobufs SOLARIS:SCLR_NETLINK:15 +} + diff --git a/src/pmdas/solaris/pmns.zfs b/src/pmdas/solaris/pmns.zfs new file mode 100644 index 0000000..6686d80 --- /dev/null +++ b/src/pmdas/solaris/pmns.zfs @@ -0,0 +1,60 @@ +zfs { + arc + used + snapshot + available SOLARIS:SCLR_ZFS:0 + quota SOLARIS:SCLR_ZFS:1 + reservation SOLARIS:SCLR_ZFS:2 + compression SOLARIS:SCLR_ZFS:3 + copies SOLARIS:SCLR_ZFS:4 + recordsize SOLARIS:SCLR_ZFS:5 + refquota SOLARIS:SCLR_ZFS:6 + refreservation SOLARIS:SCLR_ZFS:7 + referenced SOLARIS:SCLR_ZFS:8 + nsnapshots SOLARIS:SCLR_ZFS:9 +} + +zfs.used { + total SOLARIS:SCLR_ZFS:10 + byme SOLARIS:SCLR_ZFS:11 + bysnapshots SOLARIS:SCLR_ZFS:12 + bychildren SOLARIS:SCLR_ZFS:13 + byrefreservation SOLARIS:SCLR_ZFS:14 +} + +zfs.snapshot { + used SOLARIS:SCLR_ZFS:15 + referenced SOLARIS:SCLR_ZFS:16 + compression SOLARIS:SCLR_ZFS:17 +} + +zfs.arc { + size SOLARIS:SCLR_ARCSTATS:0 + min_size SOLARIS:SCLR_ARCSTATS:1 + max_size SOLARIS:SCLR_ARCSTATS:2 + mru_size SOLARIS:SCLR_ARCSTATS:3 + target_size SOLARIS:SCLR_ARCSTATS:4 + hits + misses + +} + +zfs.arc.misses { + total SOLARIS:SCLR_ARCSTATS:5 + demand_data SOLARIS:SCLR_ARCSTATS:6 + demand_metadata SOLARIS:SCLR_ARCSTATS:7 + prefetch_data SOLARIS:SCLR_ARCSTATS:8 + prefetch_metadata SOLARIS:SCLR_ARCSTATS:9 +} + +zfs.arc.hits { + total SOLARIS:SCLR_ARCSTATS:10 + mfu SOLARIS:SCLR_ARCSTATS:11 + mru SOLARIS:SCLR_ARCSTATS:12 + mfu_ghost SOLARIS:SCLR_ARCSTATS:13 + mru_ghost SOLARIS:SCLR_ARCSTATS:14 + demand_data SOLARIS:SCLR_ARCSTATS:15 + demand_metadata SOLARIS:SCLR_ARCSTATS:16 + prefetch_data SOLARIS:SCLR_ARCSTATS:17 + prefetch_metadata SOLARIS:SCLR_ARCSTATS:18 +} diff --git a/src/pmdas/solaris/pmns.zpool b/src/pmdas/solaris/pmns.zpool new file mode 100644 index 0000000..2fcae14 --- /dev/null +++ b/src/pmdas/solaris/pmns.zpool @@ -0,0 +1,31 @@ +zpool { + state SOLARIS:SCLR_ZPOOL:0 + state_int SOLARIS:SCLR_ZPOOL:1 + capacity SOLARIS:SCLR_ZPOOL:2 + used SOLARIS:SCLR_ZPOOL:3 + checksum_errors SOLARIS:SCLR_ZPOOL:4 + self_healed SOLARIS:SCLR_ZPOOL:5 + perdisk + in + out + ops +} + +zpool.in { + bytes SOLARIS:SCLR_ZPOOL:6 + ops SOLARIS:SCLR_ZPOOL:7 + errors SOLARIS:SCLR_ZPOOL:8 +} + +zpool.out { + bytes SOLARIS:SCLR_ZPOOL:9 + ops SOLARIS:SCLR_ZPOOL:10 + errors SOLARIS:SCLR_ZPOOL:11 +} + +zpool.ops { + noops SOLARIS:SCLR_ZPOOL:12 + ioctls SOLARIS:SCLR_ZPOOL:13 + claims SOLARIS:SCLR_ZPOOL:14 + frees SOLARIS:SCLR_ZPOOL:15 +} diff --git a/src/pmdas/solaris/pmns.zpool_perdisk b/src/pmdas/solaris/pmns.zpool_perdisk new file mode 100644 index 0000000..5c6dfa1 --- /dev/null +++ b/src/pmdas/solaris/pmns.zpool_perdisk @@ -0,0 +1,16 @@ +zpool.perdisk { + state SOLARIS:SCLR_ZPOOL_PERDISK:0 + state_int SOLARIS:SCLR_ZPOOL_PERDISK:1 + checksum_errors SOLARIS:SCLR_ZPOOL_PERDISK:2 + self_healed SOLARIS:SCLR_ZPOOL_PERDISK:3 + in + out +} + +zpool.perdisk.in { + errors SOLARIS:SCLR_ZPOOL_PERDISK:4 +} + +zpool.perdisk.out { + errors SOLARIS:SCLR_ZPOOL_PERDISK:5 +} diff --git a/src/pmdas/solaris/root b/src/pmdas/solaris/root new file mode 100644 index 0000000..7131985 --- /dev/null +++ b/src/pmdas/solaris/root @@ -0,0 +1,42 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include +#include "clusters.h" + +root { + kernel + disk + mem + network + hinv + zpool + zfs + pmda +} + +pmda { + uname SOLARIS:0:107 + prefetch + metric +} + +pmda.prefetch { + time SOLARIS:4095:0 + count SOLARIS:4095:1 +} + +pmda.metric { + time SOLARIS:4095:2 + count SOLARIS:4095:3 +} + +#include "pmns.kernel" +#include "pmns.disk" +#include "pmns.mem" +#include "pmns.network" +#include "pmns.hinv" +#include "pmns.zpool" +#include "pmns.zfs" +#include "pmns.zpool_perdisk" diff --git a/src/pmdas/solaris/solaris.c b/src/pmdas/solaris/solaris.c new file mode 100644 index 0000000..2deb6fd --- /dev/null +++ b/src/pmdas/solaris/solaris.c @@ -0,0 +1,216 @@ +/* + * Solaris PMDA + * + * Collect performance data from the Solaris kernel using kstat() for + * the most part. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2010 Max Matveev. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include "common.h" + +static int _isDSO = 1; +static char mypath[MAXPATHLEN]; + +/* + * wrapper for pmdaFetch which primes the methods ready for + * the next fetch + * ... real callback is fetch_callback() + */ +static int +solaris_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; + + kstat_ctl_needs_update(); + + for (i = 0; i < methodtab_sz; i++) { + methodtab[i].m_fetched = 0; + } + + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * callback provided to pmdaFetch + */ +static int +solaris_fetch_callback(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + metricdesc_t *mdp = (metricdesc_t *)mdesc->m_user; + int cluster = pmid_cluster(mdesc->m_desc.pmid); + method_t *m = methodtab + cluster; + hrtime_t start; + int rv; + __pmID_int *id = __pmid_int(&mdesc->m_desc.pmid); + + if (cluster == 4095) { + switch (id->item) { + case 0: /* pmda.prefetch.time */ + if ((inst <= 0) || (inst > methodtab_sz+1)) + return PM_ERR_INST; + atom->ull = methodtab[inst-1].m_elapsed; + return 1; + case 1: /* pmda.prefetch.count */ + if ((inst <= 0) || (inst > methodtab_sz+1)) + return PM_ERR_INST; + atom->ull = methodtab[inst-1].m_hits; + return 1; + case 2: /* pmda.metric.time */ + if ((inst <= 0) || (inst > metrictab_sz+1)) + return PM_ERR_INST; + atom->ull = metricdesc[inst-1].md_elapsed; + return 1; + case 3: /* pmda.metric.count */ + if ((inst <= 0) || (inst > metrictab_sz+1)) + return PM_ERR_INST; + atom->ull = metricdesc[inst-1].md_hits; + return 1; + default: + return PM_ERR_PMID; + } + } else if (cluster >= methodtab_sz) { + return PM_ERR_PMID; + } + + if (!m->m_fetched && m->m_prefetch) { + start = gethrtime(); + m->m_prefetch(); + m->m_elapsed = gethrtime() - start; + m->m_hits++; + m->m_fetched = 1; + } + start = gethrtime(); + rv = m->m_fetch(mdesc, inst, atom); + mdp->md_elapsed = gethrtime() - start; + mdp->md_hits++; + return rv; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +__PMDA_INIT_CALL +solaris_init(pmdaInterface *dp) +{ + if (_isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "solaris" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_3, "Solaris DSO", mypath); + } + + if (dp->status != 0) + return; + + dp->version.two.fetch = solaris_fetch; + pmdaSetFetchCallBack(dp, solaris_fetch_callback); + init_data(dp->domain); + pmdaInit(dp, indomtab, indomtab_sz, metrictab, metrictab_sz); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n\n", pmProgname); + fputs("Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -N namespace verify consistency of internal metrics with the namespace\n", + stderr); + exit(1); +} + +static void +checkname(const char *mname) +{ + int i; + for (i = 0; i < metrictab_sz; i++) { + if (strcmp(mname, metricdesc[i].md_name) == 0) + return; + } + printf ("Cannot find %s in the code\n", mname); +} + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int err = 0; + int sep = __pmPathSeparator(); + pmdaInterface desc; + int c; + char *namespace = NULL; + + _isDSO = 0; + __pmSetProgname(argv[0]); + + snprintf(mypath, sizeof(mypath), "%s%c" "solaris" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_3, pmProgname, SOLARIS, + "solaris.log", mypath); + + while ((c = pmdaGetOpt(argc, argv, "N:D:d:l:?", &desc, &err)) != EOF) { + switch (c) { + case 'N': + namespace = optarg; + break; + default: + err++; + break; + } + } + if (err) + usage(); + + if (namespace) { + if (pmLoadNameSpace(namespace)) + exit(1); + + for (c = 0; c < metrictab_sz; c++) { + char *name; + int e; + __pmID_int *id = __pmid_int(&metricdesc[c].md_desc.pmid); + id->domain = desc.domain; + + if ((e = pmNameID(metricdesc[c].md_desc.pmid, &name)) != 0) { + printf ("Cannot find %s(%s) in %s: %s\n", + metricdesc[c].md_name, + pmIDStr(metricdesc[c].md_desc.pmid), + namespace, pmErrStr(e)); + } else { + if (strcmp(name, metricdesc[c].md_name)) { + printf ("%s is %s in the %s but %s in code\n", + pmIDStr(metricdesc[c].md_desc.pmid), + name, namespace,metricdesc[c].md_name); + } + } + } + + pmTraversePMNS("", checkname); + exit (0); + } + + pmdaOpenLog(&desc); + solaris_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + + exit(0); +} diff --git a/src/pmdas/solaris/sysinfo.c b/src/pmdas/solaris/sysinfo.c new file mode 100644 index 0000000..1937eed --- /dev/null +++ b/src/pmdas/solaris/sysinfo.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2010 Max Matveev. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "common.h" +#include +#include + +typedef struct { + int fetched; + int err; + kstat_t *ksp; + cpu_stat_t cpustat; + kstat_t *info; + int info_is_good; +} ctl_t; + +static int ncpu; +static int hz; +static long pagesize; +static ctl_t *ctl; +static char uname_full[SYS_NMLN * 5]; +static int nloadavgs; +static double loadavgs[3]; + +void +sysinfo_init(int first) +{ + kstat_t *ksp; + int i; + char buf[10]; /* cpuXXXXX */ + kstat_ctl_t *kc; + + if (!first) + /* TODO ... not sure if/when we'll use this re-init hook */ + return; + + if ((kc = kstat_ctl_update()) == NULL) + return; + + for (ncpu = 0; ; ncpu++) { + ksp = kstat_lookup(kc, "cpu_stat", ncpu, NULL); + if (ksp == NULL) break; + if ((ctl = (ctl_t *)realloc(ctl, (ncpu+1) * sizeof(ctl_t))) == NULL) { + fprintf(stderr, "sysinfo_init: ctl realloc[%d] @ cpu=%d failed: %s\n", + (int)((ncpu+1) * sizeof(ctl_t)), ncpu, osstrerror()); + exit(1); + } + ctl[ncpu].info = kstat_lookup(kc, "cpu_info", ncpu, NULL); + ctl[ncpu].ksp = ksp; + ctl[ncpu].err = 0; + } + + indomtab[CPU_INDOM].it_numinst = ncpu; + indomtab[CPU_INDOM].it_set = (pmdaInstid *)malloc(ncpu * sizeof(pmdaInstid)); + /* TODO check? */ + + for (i = 0; i < ncpu; i++) { + indomtab[CPU_INDOM].it_set[i].i_inst = i; + snprintf(buf, sizeof(buf), "cpu%d", i); + indomtab[CPU_INDOM].it_set[i].i_name = strdup(buf); + /* TODO check? */ + } + + hz = (int)sysconf(_SC_CLK_TCK); + pagesize = sysconf(_SC_PAGESIZE); + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "sysinfo: ncpu=%d hz=%d\n", ncpu, hz); + } +#endif +} + +static __uint32_t +sysinfo_derived(pmdaMetric *mdesc, int inst) +{ + pmID pmid = mdesc->m_desc.pmid; + __pmID_int *ip = (__pmID_int *)&pmid; + __uint32_t val; + + ip->domain = 0; + + switch (pmid) { + + case PMDA_PMID(SCLR_SYSINFO,56): /* hinv.ncpu */ + if (inst == 0) + val = ncpu; + else + val = 0; + break; + + default: + fprintf(stderr, "cpu_derived: Botch: no method for pmid %s\n", + pmIDStr(mdesc->m_desc.pmid)); + val = 0; + break; + } + +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "cpu_derived: pmid %s inst %d val %d\n", + pmIDStr(mdesc->m_desc.pmid), inst, val); + } +#endif + + return val; +} + +void +sysinfo_prefetch(void) +{ + int i; + + nloadavgs = -1; + for (i = 0; i < ncpu; i++) { + ctl[i].fetched = 0; + ctl[i].info_is_good = 0; + } +} + +int +kstat_named_to_pmAtom(const kstat_named_t *kn, pmAtomValue *atom) +{ + static char chardat[sizeof(kn->value.c) + 1]; + + switch (kn->data_type) { + case KSTAT_DATA_UINT64: + atom->ull = kn->value.ui64; + return 1; + case KSTAT_DATA_INT64: + atom->ull = kn->value.i64; + return 1; + case KSTAT_DATA_UINT32: + atom->ull = kn->value.ui32; + return 1; + case KSTAT_DATA_INT32: + atom->ull = kn->value.i32; + return 1; + case KSTAT_DATA_STRING: + atom->cp = kn->value.str.addr.ptr; + return 1; + case KSTAT_DATA_CHAR: + memcpy(chardat, kn->value.c, sizeof(kn->value.c)); + chardat[sizeof(chardat)-1] = '\0'; + atom->cp = chardat; + return 1; + default: + return 0; + } +} + +static int +kstat_fetch_named(kstat_ctl_t *kc, pmAtomValue *atom, char *metric, + int shift_bits) +{ + kstat_t *ks; + + if ((ks = kstat_lookup(kc, "unix", -1, "system_pages")) != NULL) { + kstat_named_t *kn; + + if (kstat_read(kc, ks, NULL) == -1) + return 0; + + if (((kn = kstat_data_lookup(ks, metric)) != NULL) && + kstat_named_to_pmAtom(kn, atom)) { + atom->ull = (atom->ull * pagesize) >> shift_bits; + return 1; + } + } + return 0; +} + +int +kstat_named_to_typed_atom(const kstat_named_t *kn, int pmtype, + pmAtomValue *atom) +{ + static char chardat[sizeof(kn->value.c) + 1]; + + switch (pmtype) { + case PM_TYPE_32: + if (kn->data_type == KSTAT_DATA_INT32) { + atom->l = kn->value.i32; + return 1; + } + break; + case PM_TYPE_U32: + if (kn->data_type == KSTAT_DATA_UINT32) { + atom->ul = kn->value.ui32; + return 1; + } + break; + case PM_TYPE_64: + if (kn->data_type == KSTAT_DATA_INT64) { + atom->ll = kn->value.i64; + return 1; + } + break; + case PM_TYPE_U64: + if (kn->data_type == KSTAT_DATA_UINT64) { + atom->ull = kn->value.ui64; + return 1; + } + break; + case PM_TYPE_STRING: + switch(kn->data_type) { + case KSTAT_DATA_STRING: + atom->cp = kn->value.str.addr.ptr; + return 1; + case KSTAT_DATA_CHAR: + memcpy(chardat, kn->value.c, sizeof(kn->value.c)); + chardat[sizeof(chardat)-1] = '\0'; + atom->cp = chardat; + return 1; + } + break; + } + return 0; +} + +int +sysinfo_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) +{ + __uint64_t ull; + int i; + int ok; + ptrdiff_t offset; + struct utsname u; + kstat_ctl_t *kc; + + if ((kc = kstat_ctl_update()) == NULL) + return 0; + + /* Special processing of metrics which notionally belong + * to sysinfo category */ + switch (pmid_item(mdesc->m_desc.pmid)) { + case 109: /* hinv.physmem */ + return kstat_fetch_named(kc, atom, "physmem", 20); + case 136: /* mem.physmem */ + return kstat_fetch_named(kc, atom, "physmem", 10); + case 137: /* mem.freemem */ + return kstat_fetch_named(kc, atom, "freemem", 10); + case 138: /* mem.lotsfree */ + return kstat_fetch_named(kc, atom, "lotsfree", 10); + case 139: /* mem.availrmem */ + return kstat_fetch_named(kc, atom, "availrmem", 10); + + case 108: /* hinv.pagesize */ + atom->ul = pagesize; + return 1; + + case 107: /* pmda.uname */ + if (uname(&u) < 0) + return 0; + + snprintf(uname_full, sizeof(uname_full), "%s %s %s %s %s", + u.sysname, u.nodename, u.release, u.version, u.machine); + atom->cp = uname_full; + return 1; + case 135: /* kernel.all.load */ + if (nloadavgs < 0) { + if ((nloadavgs = getloadavg(loadavgs, 3)) < 0) + return 0; + } + + switch (inst) { + case 1: + atom->f = (float)loadavgs[LOADAVG_1MIN]; + return nloadavgs > LOADAVG_1MIN; + case 5: + atom->f = (float)loadavgs[LOADAVG_5MIN]; + return nloadavgs > LOADAVG_5MIN; + case 15: + atom->f = (float)loadavgs[LOADAVG_15MIN]; + return nloadavgs > LOADAVG_15MIN; + } + return PM_ERR_INST; + } + + ok = 1; + for (i = 0; i < ncpu; i++) { + if (inst == PM_IN_NULL || inst == i) { + if (!ctl[i].info_is_good) { + ctl[i].info_is_good = (ctl[i].info && + (kstat_read(kc, ctl[i].info, + NULL) != -1)); + } + if (ctl[i].fetched == 1) + continue; + if (kstat_read(kc, ctl[i].ksp, &ctl[i].cpustat) == -1) { + if (ctl[i].err == 0) { + fprintf(stderr, "Error: sysinfo_fetch(pmid=%s cpu=%d ...)\n", pmIDStr(mdesc->m_desc.pmid), i); + fprintf(stderr, "kstat_read(kc=%p, ksp=%p, ...) failed: %s\n", kc, ctl[i].ksp, osstrerror()); + } + ctl[i].err++; + ctl[i].fetched = -1; + ok = 0; + } + else { + ctl[i].fetched = 1; + if (ctl[i].err != 0) { + fprintf(stderr, "Success: sysinfo_fetch(pmid=%s cpu=%d ...) after %d errors as previously reported\n", + pmIDStr(mdesc->m_desc.pmid), i, ctl[i].err); + ctl[i].err = 0; + } + } + } + } + + if (!ok) + return 0; + + ull = 0; + for (i = 0; i < ncpu; i++) { + if (inst == PM_IN_NULL || inst == i) { + offset = ((metricdesc_t *)mdesc->m_user)->md_offset; + + if (offset == -1) { + ull += sysinfo_derived(mdesc, i); + } else if (offset > sizeof(ctl[i].cpustat)) { + char *stat = (char *)offset; + kstat_named_t *kn; + + if (!ctl[i].info_is_good) + return 0; + + if ((kn = kstat_data_lookup(ctl[i].info, stat)) == NULL) { + fprintf(stderr, "No kstat called %s for CPU %d\n", stat, i); + return 0; + } + + return kstat_named_to_typed_atom(kn, mdesc->m_desc.type, atom); + } else { + /* all the kstat fields are 32-bit unsigned */ + __uint32_t *ulp; + ulp = (__uint32_t *)&((char *)&ctl[i].cpustat)[offset]; + ull += *ulp; +#ifdef PCP_DEBUG + if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { + /* desperate */ + fprintf(stderr, "sysinfo_fetch: pmid %s inst %d val %u\n", + pmIDStr(mdesc->m_desc.pmid), i, *ulp); + } +#endif + } + } + } + + if (mdesc->m_desc.units.dimTime == 1) { + /* sysinfo times are in ticks, and we export as 64-bit msec */ + atom->ull = ull * 1000 / hz; + } + else if (mdesc->m_desc.type == PM_TYPE_U64) { + /* export as 64-bit value */ + atom->ull = ull; + } + else { + /* else export as a 32-bit */ + atom->ul = (__uint32_t)ull; + } + + return 1; +} diff --git a/src/pmdas/solaris/vnops.c b/src/pmdas/solaris/vnops.c new file mode 100644 index 0000000..69054d8 --- /dev/null +++ b/src/pmdas/solaris/vnops.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* kstat has counters for vnode operations for each filesystem. + * + * Unfortunately the counters for mounted fileystems are mixed with counters + * for the filesystem types and there is no obvious way to distinguish + * between the two except by trying to convert the kstat's name to the number + * and see if works */ + +#include +#include +#include +#include +#include "common.h" + +struct mountpoint { + struct mountpoint *next; + dev_t dev; + char mountpoint[]; +}; + +static struct mountpoint *mountpoints; +static struct timespec mtime; + +/* NB! The order of entires in mountopoints list is important: + * lofs mounts use the same device number but appear later + * in /etc/mnttab then the target filesystem - keeping the + * order the same as /etc/mnttab means that more "logical" + * mountpoints are reported, in particular the counters + * for "/" are not reported as /lib/libc.so.1 */ +static void +cache_mnttab(void) +{ + FILE *f; + struct mnttab m; + struct mountpoint *mp; + struct stat sb; + struct mountpoint **tail = &mountpoints; + + if (stat("/etc/mnttab", &sb) < 0) + return; + + if (mountpoints && + (sb.st_mtim.tv_sec == mtime.tv_sec) && + (sb.st_mtim.tv_nsec == mtime.tv_nsec)) + return; + + if ((f = fopen("/etc/mnttab", "r")) == NULL) + return; + + for (mp = mountpoints; mp; mp = mountpoints) { + mountpoints = mp->next; + free(mp); + } + + while(getmntent(f, &m) == 0) { + char *devop= hasmntopt(&m, "dev"); + if (devop) { + char *end; + dev_t d = strtoul(devop+4, &end, 16); + + if ((end != devop+4) && (*end != '\0')) { + fprintf(stderr, "Bogus device number %s for filesystem %s\n", + devop+4, m.mnt_mountp); + continue; + } + + mp = malloc(sizeof(*mp) + strlen(m.mnt_mountp) + 1); + if (mp == NULL) { + fprintf(stderr, + "Cannot allocate memory for cache entry of %s\n", + m.mnt_mountp); + continue; + } + mp->next = NULL; + mp->dev = d; + strcpy(mp->mountpoint, m.mnt_mountp); + *tail = mp; + tail = &mp->next; + } + } + fclose(f); + mtime = sb.st_mtim; +} + +static const char * +mountpoint_bydev(dev_t dev) +{ + int i; + for (i=0; i < 2; i++) { + struct mountpoint *mp = mountpoints; + while(mp) { + if (mp->dev == dev) + return mp->mountpoint; + mp = mp->next; + } + cache_mnttab(); + } + return NULL; +} + +int +vnops_fetch(pmdaMetric *pm, int inst, pmAtomValue *av) +{ + char *fsname; + metricdesc_t *md = pm->m_user; + kstat_t *k; + char *stat = (char *)md->md_offset; + + if (pmid_item(pm->m_desc.pmid) == 1023) { /* hinv.nfilesys */ + int sts; + sts = pmdaCacheOp(indomtab[FILESYS_INDOM].it_indom, PMDA_CACHE_SIZE_ACTIVE); + if (sts < 0) + return 0; + else { + av->ul = sts; + return 1; + } + } + + if (pmdaCacheLookup(pm->m_desc.indom, inst, &fsname, + (void **)&k) != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + if (k) { + kstat_named_t *kn = kstat_data_lookup(k, stat); + + if (kn == NULL) { + fprintf(stderr, "No kstat called %s for %s\n", stat, fsname); + return 0; + } + + return kstat_named_to_typed_atom(kn, pm->m_desc.type, av); + } + + return 0; +} + +static void +vnops_update_stats(int fetch) +{ + kstat_t *k; + kstat_ctl_t *kc = kstat_ctl_update(); + + if (kc == NULL) + return; + + for (k = kc->kc_chain; k != NULL; k = k->ks_next) { + int rv; + kstat_t *cached; + const char *key; + dev_t dev; + char *end; + pmInDom indom; + + if (strcmp(k->ks_module, "unix") || + strncmp(k->ks_name, "vopstats_", sizeof("vopstats_")-1)) + continue; + + key = k->ks_name + 9; + dev = strtoul(key, &end, 16); + if ((end != key) && (*end == '\0')) { + indom = indomtab[FILESYS_INDOM].it_indom; + if ((key = mountpoint_bydev(dev)) == NULL) + continue; + } else { + indom = indomtab[FSTYPE_INDOM].it_indom; + } + + if (pmdaCacheLookupName(indom, key, &rv, + (void **)&cached) != PMDA_CACHE_ACTIVE) { + rv = pmdaCacheStore(indom, PMDA_CACHE_ADD, key, k); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot create instance for " + "filesystem '%s': %s\n", + key, pmErrStr(rv)); + continue; + } + } + + if (fetch) + kstat_read(kc, k, NULL); + } +} + +void +vnops_refresh(void) +{ + pmdaCacheOp(indomtab[FILESYS_INDOM].it_indom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(indomtab[FSTYPE_INDOM].it_indom, PMDA_CACHE_INACTIVE); + vnops_update_stats(1); + pmdaCacheOp(indomtab[FILESYS_INDOM].it_indom, PMDA_CACHE_SAVE); + pmdaCacheOp(indomtab[FSTYPE_INDOM].it_indom, PMDA_CACHE_SAVE); +} + +void +vnops_init(int first) +{ + pmdaCacheOp(indomtab[FILESYS_INDOM].it_indom, PMDA_CACHE_LOAD); + pmdaCacheOp(indomtab[FSTYPE_INDOM].it_indom, PMDA_CACHE_LOAD); + vnops_update_stats(0); + pmdaCacheOp(indomtab[FILESYS_INDOM].it_indom, PMDA_CACHE_SAVE); + pmdaCacheOp(indomtab[FSTYPE_INDOM].it_indom, PMDA_CACHE_SAVE); +} diff --git a/src/pmdas/solaris/zfs.c b/src/pmdas/solaris/zfs.c new file mode 100644 index 0000000..9f4bc55 --- /dev/null +++ b/src/pmdas/solaris/zfs.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2009 Max Matveev. All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "common.h" + +static libzfs_handle_t *zh; +static int zf_added; + +struct zfs_data { + zfs_handle_t *zh; + uint64_t nsnaps; +}; + +/* + * For each filesystem or snapshot check if the name is in the + * corresponding instance cache. If it's not there then add it to the + * cache. If we've cached the new instance then we keep the zfs_handle + * which we've received in the argument, otherwise we need to close it + * - zfs_iter_root() expects that from us. + * + * For filesystems iterate over their snapshots and update snapshot + * count which is stored in the cached data for the instances in ZFS_INDOM + * domain. + */ +static int +zfs_cache_inst(zfs_handle_t *zf, void *arg) +{ + const char *fsname = zfs_get_name(zf); + pmInDom zfindom; + int inst, rv; + struct zfs_data *zdata = NULL; + uint64_t *snapcnt = arg; + + switch (zfs_get_type(zf)) { + case ZFS_TYPE_FILESYSTEM: + zfindom = indomtab[ZFS_INDOM].it_indom; + break; + case ZFS_TYPE_SNAPSHOT: + (*snapcnt)++; + zfindom = indomtab[ZFS_SNAP_INDOM].it_indom; + break; + default: + zfs_close(zf); + return 0; + } + + if ((rv = pmdaCacheLookupName(zfindom, fsname, &inst, + (void **)&zdata)) == PMDA_CACHE_ACTIVE) { + zfs_close(zf); + zfs_refresh_properties(zdata->zh); + zf = zdata->zh; + } else if ((rv == PMDA_CACHE_INACTIVE) && zdata) { + rv = pmdaCacheStore(zfindom, PMDA_CACHE_ADD, fsname, zdata); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot reactivate cached data for '%s': %s\n", + fsname, pmErrStr(rv)); + zfs_close(zf); + return 0; + } + zfs_close(zf); + zfs_refresh_properties(zdata->zh); + zf = zdata->zh; + } else { + if ((zdata = calloc(1, sizeof(*zdata))) == NULL) { + __pmNotifyErr(LOG_WARNING, + "Out of memory for data of %s\n", fsname); + zfs_close(zf); + return 0; + } + zdata->zh = zf; + rv = pmdaCacheStore(zfindom, PMDA_CACHE_ADD, fsname, zdata); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot cache data for '%s': %s\n", + fsname, pmErrStr(rv)); + zfs_close(zf); + return 0; + } + zf_added++; + } + + zfs_iter_filesystems(zf, zfs_cache_inst, NULL); + if (zfs_get_type(zf) == ZFS_TYPE_FILESYSTEM) { + zdata->nsnaps = 0; + zfs_iter_snapshots(zf, zfs_cache_inst, &zdata->nsnaps); + } + + return 0; +} + +void +zfs_refresh(void) +{ + zf_added = 0; + + pmdaCacheOp(indomtab[ZFS_INDOM].it_indom, PMDA_CACHE_INACTIVE); + pmdaCacheOp(indomtab[ZFS_SNAP_INDOM].it_indom, PMDA_CACHE_INACTIVE); + zfs_iter_root(zh, zfs_cache_inst, NULL); + + if (zf_added) { + pmdaCacheOp(indomtab[ZFS_INDOM].it_indom, PMDA_CACHE_SAVE); + pmdaCacheOp(indomtab[ZFS_SNAP_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} + +int +zfs_fetch(pmdaMetric *pm, int inst, pmAtomValue *atom) +{ + char *fsname; + metricdesc_t *md = pm->m_user; + struct zfs_data *zdata; + uint64_t v; + + if (pmdaCacheLookup(pm->m_desc.indom, inst, &fsname, + (void **)&zdata) != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + if (md->md_offset == -1) { /* nsnapshot */ + atom->ull = zdata->nsnaps; + return 1; + } + + v = zfs_prop_get_int(zdata->zh, md->md_offset); + + /* Special processing - compression ratio is in precent, we export + * it as multiplier */ + switch (md->md_offset) { + case ZFS_PROP_COMPRESSRATIO: + atom->d = v / 100.0; + break; + default: + atom->ull = v; + break; + } + + return 1; +} + +void +zfs_init(int first) +{ + if (zh) + return; + + zh = libzfs_init(); + if (zh) { + pmdaCacheOp(indomtab[ZFS_INDOM].it_indom, PMDA_CACHE_LOAD); + pmdaCacheOp(indomtab[ZFS_SNAP_INDOM].it_indom, PMDA_CACHE_LOAD); + zfs_iter_root(zh, zfs_cache_inst, &first); + pmdaCacheOp(indomtab[ZFS_INDOM].it_indom, PMDA_CACHE_SAVE); + pmdaCacheOp(indomtab[ZFS_SNAP_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} diff --git a/src/pmdas/solaris/zpool.c b/src/pmdas/solaris/zpool.c new file mode 100644 index 0000000..2f402f2 --- /dev/null +++ b/src/pmdas/solaris/zpool.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2009 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include "common.h" + +struct zpool_stats { + int vdev_stats_fresh; + vdev_stat_t vds; +}; + +static libzfs_handle_t *zh; +static int zp_added; + +/* + * For each zpool check the name in the instance cache, if it's not there then + * add it to the cache. Regardless if it's the first time we've seen this one + * or if it was in the cache before refresh the stats + */ +static int +zp_cache_pool(zpool_handle_t *zp, void *arg) +{ + nvlist_t *cfg = zpool_get_config(zp, NULL); + char *zpname = (char *)zpool_get_name(zp); + struct zpool_stats *zps = NULL; + pmInDom zpindom = indomtab[ZPOOL_INDOM].it_indom; + uint_t cnt = 0; + vdev_stat_t *vds; + int rv; + int inst; + nvlist_t *vdt; + + if ((rv = pmdaCacheLookupName(zpindom, zpname, &inst, + (void **)&zps)) != PMDA_CACHE_ACTIVE) { + int newpool = (zps == NULL); + + if (rv != PMDA_CACHE_INACTIVE || zps == NULL) { + zps = malloc(sizeof(*zps)); + if (zps == NULL) { + __pmNotifyErr(LOG_WARNING, + "Cannot allocate memory to hold stats for " + "zpool '%s'\n", + zpname); + goto done; + } + } + + rv = pmdaCacheStore(zpindom, PMDA_CACHE_ADD, zpname, zps); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot add '%s' to the cache " + "for instance domain %s: %s\n", + zpname, pmInDomStr(zpindom), pmErrStr(rv)); + free(zps); + goto done; + } + zp_added += newpool; + } + + rv = nvlist_lookup_nvlist(cfg, ZPOOL_CONFIG_VDEV_TREE, &vdt); + if (rv != 0) { + __pmNotifyErr(LOG_ERR, "Cannot get vdev tree for '%s': %d %d\n", + zpname, rv, oserror()); + zps->vdev_stats_fresh = 0; + } else { + /* accommodate zpool api changes ... */ +#ifdef ZPOOL_CONFIG_VDEV_STATS + rv = nvlist_lookup_uint64_array(vdt, ZPOOL_CONFIG_VDEV_STATS, + (uint64_t **)&vds, &cnt); +#else + rv = nvlist_lookup_uint64_array(vdt, ZPOOL_CONFIG_STATS, + (uint64_t **)&vds, &cnt); +#endif + if (rv == 0) { + memcpy(&zps->vds, vds, sizeof(zps->vds)); + zps->vdev_stats_fresh = 1; + } else { + __pmNotifyErr(LOG_ERR, + "Cannot get zpool stats for '%s': %d %d\n", + zpname, rv, oserror()); + zps->vdev_stats_fresh = 0; + } + } + +done: + zpool_close(zp); + return 0; +} + +void +zpool_refresh(void) +{ + zp_added = 0; + + pmdaCacheOp(indomtab[ZPOOL_INDOM].it_indom, PMDA_CACHE_INACTIVE); + zpool_iter(zh, zp_cache_pool, NULL); + + if (zp_added) { + pmdaCacheOp(indomtab[ZPOOL_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} + +int +zpool_fetch(pmdaMetric *pm, int inst, pmAtomValue *atom) +{ + struct zpool_stats *zps; + char *zpname; + metricdesc_t *md = pm->m_user; + + if (pmdaCacheLookup(indomtab[ZPOOL_INDOM].it_indom, inst, &zpname, + (void **)&zps) != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + if (zps->vdev_stats_fresh) { + switch (pmid_item(md->md_desc.pmid)) { + case 0: /* zpool.state */ + atom->cp = zpool_state_to_name(zps->vds.vs_state, zps->vds.vs_aux); + break; + case 1: /* zpool.state_int */ + atom->ul = (zps->vds.vs_aux << 8) | zps->vds.vs_state; + break; + default: + memcpy(&atom->ull, ((char *)&zps->vds) + md->md_offset, + sizeof(atom->ull)); + } + } + return zps->vdev_stats_fresh; +} + +void +zpool_init(int first) +{ + if (zh) + return; + + zh = libzfs_init(); + if (zh) { + pmdaCacheOp(indomtab[ZPOOL_INDOM].it_indom, PMDA_CACHE_LOAD); + zpool_iter(zh, zp_cache_pool, NULL); + pmdaCacheOp(indomtab[ZPOOL_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} diff --git a/src/pmdas/solaris/zpool_perdisk.c b/src/pmdas/solaris/zpool_perdisk.c new file mode 100644 index 0000000..f9646e9 --- /dev/null +++ b/src/pmdas/solaris/zpool_perdisk.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2009 Max Matveev. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#include "common.h" + +struct vdev_stats { + int vdev_stats_fresh; + vdev_stat_t vds; +}; + +static libzfs_handle_t *zh; +static int vdev_added; + +static char * +make_vdev_name(zpool_handle_t *zp, const char *pname, nvlist_t *child) +{ + char *name = NULL; + char *cname = zpool_vdev_name(zh, zp, child, B_FALSE); + uint_t size; + + if (cname == NULL) { + __pmNotifyErr(LOG_WARNING, "Cannot get the name of %s\'s " + "child\n", pname); + goto out; + } + size = strlen(pname) + strlen(cname) + 2; + name = malloc(size); + if (name == NULL) { + __pmNotifyErr(LOG_WARNING, "Cannot allocate memory for %s.%s\n", + pname, cname); + goto free_out; + } + snprintf(name, size, "%s.%s", pname, cname); +free_out: + free(cname); +out: + return name; +} + +/* + * get the names and stats of those vdevs in the pool that are disks and are + * either children, cache or spare devices + */ +static int +zp_get_vdevs(zpool_handle_t *zp, char *zpname, nvlist_t *vdt, + char ***vdev_names, + vdev_stat_t ***vds, int *num) +{ + int rv = 0; + uint_t cnt; + char **new_vdev_names; + vdev_stat_t **new_vds; + int nelem = *num; + + char *name; + vdev_stat_t *stats; + nvlist_t **children; + uint_t nchildren; + + static const char *prop[] = { + ZPOOL_CONFIG_CHILDREN, + ZPOOL_CONFIG_L2CACHE, + ZPOOL_CONFIG_SPARES + }; + int i; + int j; + char *vdev_type; + + rv = nvlist_lookup_string(vdt, ZPOOL_CONFIG_TYPE, &vdev_type); + /* we've found disk, look no further */ + if (rv == 0 && strcmp(vdev_type, "disk") == 0) { + + /* accommodate zpool api changes ... */ +#ifdef ZPOOL_CONFIG_VDEV_STATS + rv = nvlist_lookup_uint64_array(vdt, ZPOOL_CONFIG_VDEV_STATS, + (uint64_t **)&stats, &cnt); +#else + rv = nvlist_lookup_uint64_array(vdt, ZPOOL_CONFIG_STATS, + (uint64_t **)&stats, &cnt); +#endif + if (rv != 0) { + __pmNotifyErr(LOG_WARNING, "Cannot get the stats of %s\'s " + "child\n", zpname); + goto out; + } + name = make_vdev_name(zp, zpname, vdt); + if (name == NULL) { + __pmNotifyErr(LOG_WARNING, "Cannot get the name of a %s\'s " + "disk\n", zpname); + goto out; + } + nelem++; + new_vdev_names = realloc(*vdev_names, nelem * + sizeof(*new_vdev_names)); + if (new_vdev_names == NULL) { + __pmNotifyErr(LOG_WARNING, "Cannot realloc memory for %s\n", + name); + goto free_out; + } + new_vdev_names[nelem - 1] = NULL; + *vdev_names = new_vdev_names; + + new_vds = realloc(*vds, nelem * sizeof(*new_vds)); + if (new_vds == NULL) { + __pmNotifyErr(LOG_WARNING, "Cannot realloc memory for vds %s\n", + name); + goto free_out; + } + new_vds[nelem - 1] = stats; + new_vdev_names[nelem - 1] = name; + + *vds = new_vds; + *num = nelem; + goto out; + } + + /* not a disk, traversing children until we find all the disks */ + for (i = 0; i < sizeof(prop) / sizeof(prop[0]); i++) { + rv = nvlist_lookup_nvlist_array(vdt, prop[i], &children, + &nchildren); + if (rv != 0) + nchildren = 0; + for (j = 0; j < nchildren; j++) { + zp_get_vdevs(zp, zpname, children[j], vdev_names, vds, num); + } + } + return 0; +out: + return rv; +free_out: + free(name); + return rv; +} + +/* + * For each zpool, check the leaf vdev names that are disks in the instance + * cache, if one is not there then add it to the cache. Regardless if it's the + * first time we've seen it or if it was in the cache before refresh the stats + */ +static int +zp_cache_vdevs(zpool_handle_t *zp, void *arg) +{ + nvlist_t *cfg = zpool_get_config(zp, NULL); + char *zpname = (char *)zpool_get_name(zp); + struct vdev_stats *zps = NULL; + pmInDom zpindom = indomtab[ZPOOL_PERDISK_INDOM].it_indom; + int rv; + int inst; + nvlist_t *vdt; + + int i; + char **vdev_names = NULL; + vdev_stat_t **vds = NULL; + int num = 0; + + rv = nvlist_lookup_nvlist(cfg, ZPOOL_CONFIG_VDEV_TREE, &vdt); + if (rv != 0) { + __pmNotifyErr(LOG_ERR, "Cannot get vdev tree for '%s': %d %d\n", + zpname, rv, oserror()); + goto done; + } + + rv = zp_get_vdevs(zp, zpname, vdt, &vdev_names, &vds, &num); + if (rv != 0) { + __pmNotifyErr(LOG_WARNING, "Cannot get vdevs for zpool '%s'\n", + zpname); + goto free_done; + } + + for (i = 0; i < num; i++) { + if (vdev_names[i] == NULL) + continue; + rv = pmdaCacheLookupName(zpindom, vdev_names[i], &inst, + (void **)&zps); + if (rv != PMDA_CACHE_ACTIVE) { + int new_vdev = (zps == NULL); + + if (rv != PMDA_CACHE_INACTIVE || new_vdev) { + zps = malloc(sizeof(*zps)); + if (zps == NULL) { + __pmNotifyErr(LOG_WARNING, + "Cannot allocate memory to hold stats for " + "vdev '%s'\n", vdev_names[i]); + goto free_done; + } + } + + rv = pmdaCacheStore(zpindom, PMDA_CACHE_ADD, vdev_names[i], + zps); + if (rv < 0) { + __pmNotifyErr(LOG_WARNING, + "Cannot add '%s' to the cache " + "for instance domain %s: %s\n", + vdev_names[i], pmInDomStr(zpindom), + pmErrStr(rv)); + free(zps); + goto free_done; + } + vdev_added += new_vdev; + } + + if (rv >= 0) { + memcpy(&zps->vds, vds[i], sizeof(zps->vds)); + zps->vdev_stats_fresh = 1; + } else { + __pmNotifyErr(LOG_ERR, + "Cannot get stats for '%s': %d %d\n", + vdev_names[i], rv, oserror()); + zps->vdev_stats_fresh = 0; + } + } +free_done: + for (i = 0; i < num; i++) + free(vdev_names[i]); + free(vdev_names); + free(vds); +done: + zpool_close(zp); + return 0; +} + +void +zpool_perdisk_refresh(void) +{ + vdev_added = 0; + + pmdaCacheOp(indomtab[ZPOOL_PERDISK_INDOM].it_indom, PMDA_CACHE_INACTIVE); + zpool_iter(zh, zp_cache_vdevs, NULL); + + if (vdev_added) { + pmdaCacheOp(indomtab[ZPOOL_PERDISK_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} + +int +zpool_perdisk_fetch(pmdaMetric *pm, int inst, pmAtomValue *atom) +{ + struct vdev_stats *stats; + char *vdev_name; + metricdesc_t *md = pm->m_user; + + if (pmdaCacheLookup(indomtab[ZPOOL_PERDISK_INDOM].it_indom, inst, + &vdev_name, (void **)&stats) != PMDA_CACHE_ACTIVE) + return PM_ERR_INST; + + if (stats->vdev_stats_fresh) { + switch (pmid_item(md->md_desc.pmid)) { + case 0: /* zpool.perdisk.state */ + atom->cp = zpool_state_to_name(stats->vds.vs_state, + stats->vds.vs_aux); + break; + case 1: /* zpool.perdisk.state_int */ + atom->ul = (stats->vds.vs_aux << 8) | stats->vds.vs_state; + break; + default: + memcpy(&atom->ull, ((char *)&stats->vds) + md->md_offset, + sizeof(atom->ull)); + } + } + + return stats->vdev_stats_fresh; +} + +void +zpool_perdisk_init(int first) +{ + if (zh) + return; + + zh = libzfs_init(); + if (zh) { + pmdaCacheOp(indomtab[ZPOOL_PERDISK_INDOM].it_indom, PMDA_CACHE_LOAD); + zpool_iter(zh, zp_cache_vdevs, NULL); + pmdaCacheOp(indomtab[ZPOOL_PERDISK_INDOM].it_indom, PMDA_CACHE_SAVE); + } +} diff --git a/src/pmdas/summary/GNUmakefile b/src/pmdas/summary/GNUmakefile new file mode 100644 index 0000000..092e30d --- /dev/null +++ b/src/pmdas/summary/GNUmakefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = summary +DOMAIN = SYSSUMMARY +TARGETS = $(IAM)$(EXECSUFFIX) + +HFILES = summary.h +CFILES = summary.c pmda.c mainloop.c + +LSRCFILES = Install README Remove help pmns root summary.pmie +LLDFLAGS= -L$(TOPDIR)/src/libpcp/src -L$(TOPDIR)/src/libpcp_pmda/src +LLDLIBS = $(PCP_PMDALIB) +LDIRT = domain.h *.log *.dir *.pag $(TARGETS) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +default: build-me + +include $(TOPDIR)/src/include/buildrules + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(TARGETS) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 root README help pmns domain.h $(PMDADIR) + $(INSTALL) -m 644 summary.pmie $(PMDADIR)/expr.pmie +else +build-me: +install: +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +mainloop.o summary.o pmda.o: summary.h +summary.o pmda.o: domain.h + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_%: default + @true + +install_%: install + @true diff --git a/src/pmdas/summary/Install b/src/pmdas/summary/Install new file mode 100644 index 0000000..590247a --- /dev/null +++ b/src/pmdas/summary/Install @@ -0,0 +1,51 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the summary PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=summary +pmda_interface=2 +forced_restart=false + +pmdaSetup + +if $do_pmda +then + if [ ! -x $PCP_BIN_DIR/pmie ] + then + echo \ +'Error: The "summary" PMDA requires the pmie(1) application but this + does not appear to be installed.' + exit 1 + fi + + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N "Interval between summary expression evaluation (seconds)? [10] ""$PCP_ECHO_C" + read delta + [ -z "$delta" ] && delta=10 + [ -z "`echo $delta | tr -d '[0-9]'`" ] && break + echo "Error: interval \"$delta\" must be an integer, please try again" + done + args="$PCP_BIN_DIR/pmie -x -t $delta $PCP_PMDAS_DIR/summary/expr.pmie" + check_delay=15 +fi + +pmdaInstall + +exit 0 diff --git a/src/pmdas/summary/README b/src/pmdas/summary/README new file mode 100644 index 0000000..c07fddd --- /dev/null +++ b/src/pmdas/summary/README @@ -0,0 +1,255 @@ +Performance Co-Pilot PMDA for Exporting Metric Summaries +======================================================== + +This Performance Metrics Domain Agent (PMDA) is capable of collecting +performance metrics values from other PMDAs, computing derived +(summary) values, and exporting these derived values as performance +metrics. + +This agent uses the Performance Metrics Inference Engine pmie(1) to +periodically collect the data and compute the summary values. These +derived values are typically computed by expressions that aggregate a +number of base performance values, perhaps from a number of subsystems +on the one host or even from multiple hosts, and perhaps over an +extended period of time. + +All of the exported metrics have a singular instance and the values are +"instantaneous", i.e. the exported value is the value as of the last +time the summary was computed. Refer to the PMAPI(3) man page for more +information about these terms. + +Metrics +======= + +See the file ./help, or install the agent and execute the command + + $ pminfo -fT summary + +Note that customization of the metrics made available by the summary +PMDA is possible, as described below. + +Installation +============ + + + # cd $PCP_PMDAS_DIR/summary + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + This PMDA caches the most recent value for the performance metrics + computed by pmie(1). The cached values are the ones returned via + the Performance Metrics Collection Demon pmcd(1) to clients. By + default pmie(1) evaluates the expressions once every 10 seconds. + The installation procedure will offer you the option to change this + interval. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + You will be prompted for the necessary information to set up + the summary agent. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/summary + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/summary.log) should be checked for any warnings + or errors. + +Customization +============= + +New summary metrics may be added as follows. + + + Choose new Performance Metric Name Space (PMNS) names for the new + metrics. These must begin with "summary." and follow the rules + described in pmns(4). + + For example summary.fs.wr_cache_hit and summary.fs.rd_cache_hit + + + Edit the file ./pmns to add the new PMNS names in the format + described in pmns(4). You must choose a unique Performance Metric + Id (PMID) for each metric ... in the ./pmns file these will appear + as SYSSUMMARY:0:x for some x that is arbitrary in the range 0 to + 1023 and unique in this file. + + For example + + summary { + cpu + disk + netif + /*new*/ fs + } + + ... + + summary.fs { + wr_cache_hit SYSSUMMARY:0:10 + rd_cache_hit SYSSUMMARY:0:11 + } + + + Use the local fake PMNS ./root and validate that the PMNS changes + are correct. + + For example + + $ pminfo -n root -m summary.fs + summary.fs.wr_cache_hit PMID: 27.0.10 + summary.fs.rd_cache_hit PMID: 27.0.11 + + + Create a file (./expr.pmie in the examples below) containing the + new expressions. If the name to the left of the assignment + operator (=) is one of the PMNS names, then the pmie(1) expression + to the right will be evaluated and returned by the summary PMDA. + The expression must return a numeric value, which is exported as a + double precision floating point number. + + If the expression has a set value, then only the first value is + exported (in most cases, the pmie aggregate operators should be used + to produce a scalar sum or average from a set of numeric values). + + The exported metric has the dimension (space, time and count) of + the expression, and the scale is the canonical scale used by + pmie(1), namely bytes, seconds and counts. + + For example + + // filesystem buffer cache hit percentages + prefix = "kernel.all.io"; // variable, not exported + summary.fs.wr_cache_hit = + 100 - 100 * $prefix.bwrite / $prefix.lwrite; + summary.fs.rd_cache_hit = + 100 - 100 * $prefix.bread / $prefix.lread; + + + Run pmie in debug mode to verify the expressions are being + evaluated correctly, and the values make sense. + + For example + + $ pmie -t2 -v expr.pmie + summary.fs.wr_cache_hit: ? + summary.fs.rd_cache_hit: ? + + summary.fs.wr_cache_hit: 45.83 + summary.fs.rd_cache_hit: 83.2 + + summary.fs.wr_cache_hit: 39.22 + summary.fs.rd_cache_hit: 84.51 + + Once you are happy with the new expressions, add them to the existing + expressions in the file ./summary.pmie. + + + Edit the ./help file to add help text for the new metrics ... see + newhelp(1) for a description of the syntax. + + For example + + @ summary.fs.wr_cache_hit Filesystem cache read hit ratio + Percentage of filesystem block writes that involve a block currently + found in the filesystem cache. + + @ summary.fs.rd_cache_hit Filesystem cache write hit ratio + Percentage of filesystem block reads that involve a block currently + found in the filesystem cache, and thereby avoid a physical read. + + + Install the new PMDA + + For example + + # ./Install + + You will need to choose an appropriate configuration for installation of + the "summary" Performance Metrics Domain Agent (PMDA). + + collector collect performance statistics on this system + monitor allow this system to monitor local and/or remote systems + both collector and monitor configuration for this system + Please enter c(ollector) or m(onitor) or b(oth) [b] + + Updating the Performance Metrics Name Space ... + Installing pmchart view(s) ... + + Interval between summary expression evaluation (seconds)? [10] + Terminate PMDA if already installed ... + Installing files .. + rm -f help.pag help.dir + $PCP_BINADM_DIR/newhelp help + Updating the PMCD control file, and notifying PMCD ... + Wait 15 seconds for the agent to initialize ... + Check summary metrics have appeared ... 8 metrics and 8 values + + + Check the metrics ... + + For example + + $ pminfo -fT summary.fs + + summary.fs.wr_cache_hit + Help: + Percentage of filesystem block writes that involve a block currently + found in the filesystem cache. + value 11.97916666666666 + + summary.fs.rd_cache_hit + Help: + Percentage of filesystem block reads that involve a block currently + found in the filesystem cache, and thereby avoid a physical read. + value 74.31192660550458 + + $ pmval -t5 -s4 summary.fs.wr_cache_hit + + metric: summary.fs.wr_cache_hit + host: localhost + semantics: instantaneous value + units: none + samples: 8 + interval: 5.00 sec + + 63.60132158590308 + 62.71878646441073 + 62.71878646441073 + 58.73968492123031 + 58.73968492123031 + 65.33822758259046 + 65.33822758259046 + 72.6099706744868 + + Note the values are being sampled here by pmval(1) every 5 seconds, + but pmie(1) is only passing new values to the sample PMDA every + 10 seconds. Both rates could be changed to suit the dynamics of + your new metrics. + + + Create pmchart(1) views, pmview(1) scenes and pmlogger(1) + configurations to monitor and archive your new performance + metrics. + + For example, a pmchart view could be created using pmchart and + the View->Save Configuration menu option. Copy the view from + $HOME/.pcp/pmchart into $PCP_PMDAS_DIR/summary and rename it to have + the suffix ".pmchart", and a prefix that identifies the view and + is unique amongst the view names in $PCP_VAR_DIR/config/pmchart, + e.g. Summary.FScache.pmchart + + Then + # cp Summary.FScache.pmchart $PCP_VAR_DIR/config/pmchart/Summary.FScache + + will install the view in a place where pmchart(1) will be able + to find it. Provided the name of the file ends in ".pmchart" in + $PCP_PMDAS_DIR/summary the view will be re-installed as a side-effect + of any subsequent ./Install of the summary PMDA. diff --git a/src/pmdas/summary/Remove b/src/pmdas/summary/Remove new file mode 100644 index 0000000..0ab6344 --- /dev/null +++ b/src/pmdas/summary/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the summary PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=summary + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/summary/help b/src/pmdas/summary/help new file mode 100644 index 0000000..5130a10 --- /dev/null +++ b/src/pmdas/summary/help @@ -0,0 +1,56 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# summary PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ summary.cpu.util CPU utilization +Fraction of time spent executing in user or system mode, averaged +over all CPUs. Any time not accountered for here is spent either +idle or waiting for I/O. Value in the range 0 to 1. + +@ summary.cpu.busy Proportion of the CPUs that are busy +Fraction of the CPUs busy executing in user and/or system mode for more +than 70% of the time. Value in the range 0 to 1. + +@ summary.disk.iops Average disk IOPS +Average disk throughput in IO operations per second. + +@ summary.disk.busy Proportion of the disks that are busy +Fraction of the disks busy serving at least 30 IOPs. Value in the +range 0 to 1. + +@ summary.netif.packets Average network interface throughput +Average network interface throughput in packets per second. + +@ summary.netif.busy Proportion of the network interfaces that are busy +Fraction of network interfaces busy serving at least 375 packets per +second). Value in the range 0 to 1. + diff --git a/src/pmdas/summary/mainloop.c b/src/pmdas/summary/mainloop.c new file mode 100644 index 0000000..e59a467 --- /dev/null +++ b/src/pmdas/summary/mainloop.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "summary.h" + +static void (*freeResultCallback)(pmResult *) = __pmFreeResultValues; + +void +mainLoopFreeResultCallback(void (*callback)(pmResult *res)) +{ + freeResultCallback = callback; +} + +void +summaryMainLoop(char *pmdaname, int clientfd, pmdaInterface *dtp) +{ + __pmPDU *pb_pmcd; + __pmPDU *pb_client; + int sts; + pmID pmid; + pmDesc desc; + int npmids; + pmID *pmidlist; + pmResult *result; + int ctxnum; + __pmTimeval when; + int ident; + int type; + pmInDom indom; + int inst; + char *name; + __pmInResult *inres; + char *buffer; + __pmProfile *profile; + __pmProfile *saveprofile = NULL; + static fd_set readFds; + int maxfd; + int clientReady, pmcdReady; + int infd, outfd; + + if (dtp->comm.pmda_interface != PMDA_INTERFACE_2) { + __pmNotifyErr(LOG_CRIT, + "summaryMainLoop supports PMDA protocol version 2 only, " + "not %d\n", dtp->comm.pmda_interface); + exit(1); + } else { + infd = dtp->version.two.ext->e_infd; + outfd = dtp->version.two.ext->e_outfd; + } + + maxfd = infd+1; + if (clientfd >= maxfd) + maxfd = clientfd+1; + + for ( ;; ) { + FD_ZERO(&readFds); + FD_SET(infd, &readFds); + FD_SET(clientfd, &readFds); + + /* select here : block if nothing to do */ + sts = select(maxfd, &readFds, NULL, NULL, NULL); + + clientReady = FD_ISSET(clientfd, &readFds); + pmcdReady = FD_ISSET(infd, &readFds); + + if (sts < 0) + break; + if (sts == 0) + continue; + + if (clientReady) { + /* + * Service the command/client + */ + sts = __pmGetPDU(clientfd, ANY_SIZE, TIMEOUT_NEVER, &pb_client); + if (sts < 0) + __pmNotifyErr(LOG_ERR, "client __pmGetPDU: %s\n", pmErrStr(sts)); + if (sts <= 0) + /* End of File or error */ + goto done; + + service_client(pb_client); + __pmUnpinPDUBuf(pb_client); + } + + if (pmcdReady) { + /* service pmcd */ + sts = __pmGetPDU(infd, ANY_SIZE, TIMEOUT_NEVER, &pb_pmcd); + + if (sts < 0) + __pmNotifyErr(LOG_ERR, "__pmGetPDU: %s\n", pmErrStr(sts)); + if (sts <= 0) + /* End of File or error */ + goto done; + + switch (sts) { + + case PDU_PROFILE: + /* + * can ignore ctxnum, since pmcd has already used this to send + * the correct profile, if required + */ + if ((sts = __pmDecodeProfile(pb_pmcd, &ctxnum, &profile)) >= 0) + sts = dtp->version.two.profile(profile, + dtp->version.two.ext); + if (sts < 0) + __pmSendError(outfd, FROM_ANON, sts); + else { + if (saveprofile != NULL) + free(saveprofile); + /* + * need to keep the last valid one around, as the DSO + * routine just remembers the address + */ + saveprofile = profile; + } + break; + + case PDU_FETCH: + /* + * can ignore ctxnum, since pmcd has already used this to send + * the correct profile, if required + */ + sts = __pmDecodeFetch(pb_pmcd, &ctxnum, &when, &npmids, &pmidlist); + + /* Ignore "when"; pmcd should intercept archive log requests */ + if (sts >= 0) { + sts = dtp->version.two.fetch(npmids, pmidlist, &result, + dtp->version.two.ext); + __pmUnpinPDUBuf(pmidlist); + } + if (sts < 0) + __pmSendError(outfd, FROM_ANON, sts); + else { + int st; + st =__pmSendResult(outfd, FROM_ANON, result); + if (st < 0) { + __pmNotifyErr(LOG_ERR, + "Cannot send fetch result: %s\n", + pmErrStr(st)); + } + (*freeResultCallback)(result); + } + break; + + case PDU_DESC_REQ: + if ((sts = __pmDecodeDescReq(pb_pmcd, &pmid)) >= 0) { + sts = dtp->version.two.desc(pmid, &desc, + dtp->version.two.ext); + } + if (sts < 0) + __pmSendError(outfd, FROM_ANON, sts); + else + __pmSendDesc(outfd, FROM_ANON, &desc); + break; + + case PDU_INSTANCE_REQ: + if ((sts = __pmDecodeInstanceReq(pb_pmcd, &when, &indom, &inst, &name)) >= 0) { + /* + * Note: when is ignored. + * If we get this far, we are _only_ dealing + * with current data (pmcd handles the other + * cases). + */ + sts = dtp->version.two.instance(indom, inst, name, + &inres, + dtp->version.two.ext); + } + if (sts < 0) + __pmSendError(outfd, FROM_ANON, sts); + else { + __pmSendInstance(outfd, FROM_ANON, inres); + __pmFreeInResult(inres); + } + break; + + case PDU_TEXT_REQ: + if ((sts = __pmDecodeTextReq(pb_pmcd, &ident, &type)) >= 0) { + sts = dtp->version.two.text(ident, type, &buffer, + dtp->version.two.ext); + } + if (sts < 0) + __pmSendError(outfd, FROM_ANON, sts); + else + __pmSendText(outfd, FROM_ANON, ident, buffer); + break; + + case PDU_RESULT: + if ((sts = __pmDecodeResult(pb_pmcd, &result)) >= 0) + sts = dtp->version.two.store(result, + dtp->version.two.ext); + __pmSendError(outfd, FROM_ANON, sts); + pmFreeResult(result); + break; + + case PDU_ERROR: + /* end of context from PMCD ... we don't care */ + break; + + default: + fprintf(stderr, "%s: bogus pdu type: 0x%0x?\n", pmdaname, sts); + __pmSendError(outfd, FROM_ANON, PM_ERR_NYI); + break; + } + __pmUnpinPDUBuf(pb_pmcd); + } + } + +done: + return; +} diff --git a/src/pmdas/summary/pmda.c b/src/pmdas/summary/pmda.c new file mode 100644 index 0000000..2a8828c --- /dev/null +++ b/src/pmdas/summary/pmda.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012 Red Hat. + * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include +#include "summary.h" +#include "domain.h" + +extern void summary_init(pmdaInterface *); +extern void summary_done(void); + +pid_t clientPID; + +int +main(int argc, char **argv) +{ + int errflag = 0; + int sep = __pmPathSeparator(); + char **commandArgv; + pmdaInterface dispatch; + int i; + int len, c; + int clientPipe[2]; + char helpfile[MAXPATHLEN]; + int cmdpipe; /* metric source/cmd pipe */ + char *command = NULL; + char *username; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + __pmSetInternalState(PM_STATE_PMCS); /* we are below the PMAPI */ + + snprintf(helpfile, sizeof(helpfile), "%s%c" "summary" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon (&dispatch, PMDA_INTERFACE_2, pmProgname, SYSSUMMARY, + "summary.log", helpfile); + + while ((c = pmdaGetOpt(argc, argv, "H:h:D:d:l:U:", + &dispatch, &errflag)) != EOF) { + switch (c) { + + case 'H': /* backwards compatibility, synonym for -h */ + dispatch.version.two.ext->e_helptext = optarg; + break; + + case 'U': + username = optarg; + break; + + case '?': + errflag++; + break; + } + } + + for (len=0, i=optind; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + if (len == 0) { + fprintf(stderr, "%s: a command must be given after the options\n", + pmProgname); + errflag++; + } + else { + command = (char *)malloc(len+2); + command[0] = '\0'; + for (i=optind; i < argc; i++) { + if (i > optind) + strcat(command, " "); + strcat(command, argv[i]); + } + } + commandArgv = argv + optind; + + if (errflag) { + fprintf(stderr, "Usage: %s [options] command [arg ...]\n\n", + pmProgname); + fputs("Options:\n" + " -h helpfile help text file\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -U username user account to run under (default \"pcp\")\n", + stderr); + exit(1); + } + + /* force errors from here on into the log */ + pmdaOpenLog(& dispatch); + + /* switch to alternate user account now */ + __pmSetProcessIdentity(username); + + /* initialize */ + summary_init(&dispatch); + + pmdaConnect(&dispatch); + if (dispatch.status) { + fprintf (stderr, "Cannot connect to pmcd: %s\n", + pmErrStr(dispatch.status)); + exit (1); + } + + /* + * open a pipe to the command + */ + if (pipe1(clientPipe) < 0) { + perror("pipe"); + exit(oserror()); + } + + if ((clientPID = fork()) == 0) { + /* child */ + char cmdpath[MAXPATHLEN+5]; + close(clientPipe[0]); + if (dup2(clientPipe[1], fileno(stdout)) < 0) { + perror("dup"); + exit(oserror()); + } + close(clientPipe[1]); + + snprintf (cmdpath, sizeof(cmdpath), "exec %s", commandArgv[0]); + execv(commandArgv[0], commandArgv); + + perror(cmdpath); + exit(oserror()); + } + + fprintf(stderr, "clientPID = %" FMT_PID "\n", clientPID); + + close(clientPipe[1]); + cmdpipe = clientPipe[0]; /* parent/agent reads from here */ + __pmSetVersionIPC(cmdpipe, PDU_VERSION2); + + summaryMainLoop(pmProgname, cmdpipe, &dispatch); + + summary_done(); + exit(0); +} diff --git a/src/pmdas/summary/pmns b/src/pmdas/summary/pmns new file mode 100644 index 0000000..44f89fe --- /dev/null +++ b/src/pmdas/summary/pmns @@ -0,0 +1,24 @@ +/* + * Metrics for summary PMDA + */ + +summary { + cpu + disk + netif +} + +summary.cpu { + util SYSSUMMARY:0:1 + busy SYSSUMMARY:0:2 +} + +summary.disk { + iops SYSSUMMARY:0:3 + busy SYSSUMMARY:0:4 +} + +summary.netif { + packets SYSSUMMARY:0:6 + busy SYSSUMMARY:0:7 +} diff --git a/src/pmdas/summary/root b/src/pmdas/summary/root new file mode 100644 index 0000000..12754f3 --- /dev/null +++ b/src/pmdas/summary/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { summary } + +#include "pmns" + diff --git a/src/pmdas/summary/summary.c b/src/pmdas/summary/summary.c new file mode 100644 index 0000000..a3da84a --- /dev/null +++ b/src/pmdas/summary/summary.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 1995,2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "summary.h" +#include "domain.h" +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +int nmeta; +meta_t *meta; +pmResult *cachedResult; +static int *freeList; + +static int +summary_desc(pmID pmid, pmDesc *desc, pmdaExt * ex) +{ + static int i=0; + + if ( i < nmeta && pmid == meta[i].desc.pmid) { +found: + if (meta[i].desc.type == PM_TYPE_NOSUPPORT) + return PM_ERR_AGAIN; /* p76 rules ok */ + *desc = meta[i].desc; /* struct assignment */ + return 0; /* success */ + } + + for (i = 0; i < nmeta; i++) { + if (pmid == meta[i].desc.pmid) + goto found; + } + return PM_ERR_PMID; +} + +void +service_client(__pmPDU *pb) +{ + int n; + int i; + int j; + pmDesc desc; + pmDesc foundDesc; + pmResult *resp; + pmValueSet *vsp; + __pmPDUHdr *ph = (__pmPDUHdr *)pb; + + switch (ph->type) { + + case PDU_DESC: + if ((n = __pmDecodeDesc(pb, &desc)) < 0) { + fprintf(stderr, "service_client: __pmDecodeDesc failed: %s\n", + pmErrStr(n)); + exit(1); + } + + if (desc.indom != PM_INDOM_NULL) { + fprintf(stderr, "service_client: Warning: ignored desc for pmid=%s: indom is not singular\n", pmIDStr(desc.pmid)); + return; + } + + if (summary_desc(desc.pmid, &foundDesc, NULL) == 0) { + /* already in table */ + fprintf(stderr, + "service_client: Warning: duplicate desc for pmid=%s\n", + pmIDStr(desc.pmid)); + return; + } + + nmeta++; + if ((meta = (meta_t *)realloc(meta, nmeta * sizeof(meta_t))) == NULL) { + __pmNoMem("service_client: meta realloc", nmeta * sizeof(meta_t), PM_FATAL_ERR); + } + memcpy(&meta[nmeta-1].desc, &desc, sizeof(pmDesc)); + + break; + + case PDU_RESULT: + if ((n = __pmDecodeResult(pb, &resp)) < 0) { + fprintf(stderr, "service_client: __pmDecodeResult failed: %s\n", pmErrStr(n)); + exit(1); + } + + if (cachedResult == NULL) { + int need; + need = (int)sizeof(pmResult) - (int)sizeof(pmValueSet *); + if ((cachedResult = (pmResult *)malloc(need)) == NULL) { + __pmNoMem("service_client: result malloc", need, PM_FATAL_ERR); + } + cachedResult->numpmid = 0; + } + + /* + * swap values from resp with those in cachedResult, expanding + * cachedResult if there are metrics we've not seen before + */ + for (i = 0; i < resp->numpmid; i++) { + for (j = 0; j < cachedResult->numpmid; j++) { + if (resp->vset[i]->pmid == cachedResult->vset[j]->pmid) { + /* found matching PMID, update this value */ + break; + } + } + + if (j == cachedResult->numpmid) { + /* new PMID, expand cachedResult and initialize vset */ + int need; + cachedResult->numpmid++; + need = (int)sizeof(pmResult) + + (cachedResult->numpmid-1) * (int)sizeof(pmValueSet *); + if ((cachedResult = (pmResult *)realloc(cachedResult, need)) == NULL) { + __pmNoMem("service_client: result realloc", need, PM_FATAL_ERR); + } + if ((cachedResult->vset[j] = (pmValueSet *)malloc(sizeof(pmValueSet))) == NULL) { + __pmNoMem("service_client: vset[]", sizeof(pmValueSet), PM_FATAL_ERR); + } + cachedResult->vset[j]->pmid = resp->vset[i]->pmid; + cachedResult->vset[j]->numval = 0; + } + + /* + * swap vsets + */ + vsp = cachedResult->vset[j]; + cachedResult->vset[j] = resp->vset[i]; + resp->vset[i] = vsp; + + } + + pmFreeResult(resp); + break; + + case PDU_ERROR: + if ((n = __pmDecodeError(pb, &i)) < 0) { + fprintf(stderr, "service_client: __pmDecodeError failed: %s\n", pmErrStr(n)); + exit(1); + } + fprintf(stderr, "service_client: Error PDU! %s\n", pmErrStr(i)); + break; + + default: + fprintf(stderr, "service_client: Bogus PDU type %d\n", ph->type); + exit(1); + } +} + +static int +summary_profile(__pmProfile *prof, pmdaExt * ex) +{ + /* + * doesn't make sense since summary metrics + * always have a singular instance domain. + */ + return 0; +} + +static int +summary_instance(pmInDom indom, int inst, char *name, __pmInResult **result, + pmdaExt * ex) +{ + return PM_ERR_INDOM; +} + +static void +freeResultCallback(pmResult *res) +{ + int i; + + /* + * pmResult has now been sent to pmcd. Only free the + * value sets that had no values available because + * the valid ones were reused from the cachedResult. + */ + for (i=0; i < res->numpmid; i++) { + if (freeList[i]) + free(res->vset[i]); + } + return; +} + + +static int +summary_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt * ex) +{ + int i; /* over pmidlist[] */ + int j; /* over vset->vlist[] */ + int sts; + int need; + int validpmid; + pmID pmid; + pmDesc desc; + static pmResult *res = NULL; + static int maxnpmids = 0; + + if (numpmid > maxnpmids) { + maxnpmids = numpmid; + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = sizeof(pmResult) + (numpmid - 1) * sizeof(pmValueSet *); + if ((res = (pmResult *)malloc(need)) == NULL) + return -oserror(); + + if (freeList != NULL) + free(freeList); + if ((freeList = (int *)malloc(numpmid * sizeof(int))) == NULL) + return -oserror(); + } + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + for (i = 0; i < numpmid; i++) { + pmid = pmidlist[i]; + + /* + * do we know about the descriptor for this pmid? + * If not, then the error is PM_ERR_PMID + * regardless of whether there is an entry in + * the cached result. + */ + sts = summary_desc(pmid, &desc, NULL); + validpmid = (sts == 0); + + res->vset[i] = NULL; + freeList[i] = 1; + + if (validpmid && cachedResult != NULL) { + for (j=0; j < cachedResult->numpmid; j++) { + if (pmid == cachedResult->vset[j]->pmid) { + res->vset[i] = cachedResult->vset[j]; + freeList[i] = 0; + break; + } + } + } + + if (!validpmid || res->vset[i] == NULL) { + /* no values available or the metric has no descriptor */ + if ((res->vset[i] = (pmValueSet *)malloc(sizeof(pmValueSet))) == NULL) + return -oserror(); + res->vset[i]->pmid = pmid; + res->vset[i]->valfmt = PM_VAL_INSITU; + res->vset[i]->numval = validpmid ? PM_ERR_VALUE : sts; + } + } + *resp = res; + + return numpmid; +} + +static int +summary_store(pmResult *result, pmdaExt * ex) +{ + return PM_ERR_PERMISSION; +} + +void +summary_init(pmdaInterface *dp) +{ + void (*callback)() = freeResultCallback; + + dp->version.two.profile = summary_profile; + dp->version.two.fetch = summary_fetch; + dp->version.two.desc = summary_desc; + dp->version.two.instance = summary_instance; + dp->version.two.store = summary_store; + + mainLoopFreeResultCallback(callback); + + pmdaInit(dp, NULL, 0, NULL, 0); +} + +void +summary_done(void) +{ + int st; + + fprintf(stderr, "summary agent pid=%" FMT_PID " done\n", getpid()); + kill(clientPID, SIGINT); + waitpid(clientPID, &st, 0); +} diff --git a/src/pmdas/summary/summary.h b/src/pmdas/summary/summary.h new file mode 100644 index 0000000..ccc0613 --- /dev/null +++ b/src/pmdas/summary/summary.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * exported summary metrics + */ +typedef struct { + char *name; /* name space path */ + pmDesc desc; /* descriptor, inc. pmid */ +} meta_t; + +extern meta_t *meta; +extern int nmeta; +extern char *command; +extern char *helpfile; +extern int cmdpipe; +extern pid_t clientPID; + +extern void summaryMainLoop(char *, int, pmdaInterface *); +extern void mainLoopFreeResultCallback(void (*)(pmResult *)); +extern void service_client(__pmPDU *); + + diff --git a/src/pmdas/summary/summary.pmie b/src/pmdas/summary/summary.pmie new file mode 100644 index 0000000..bedb824 --- /dev/null +++ b/src/pmdas/summary/summary.pmie @@ -0,0 +1,40 @@ +// +// summary metrics +// +// these expressions are evaluated by pmie(1) +// +// use the default interval between expression evaluation (currently +// 10 seconds), and re-configure via -t command line arg to pmie +// (see Install) + +// CPU utilization +// +cpuse = "(kernel.percpu.cpu.sys + kernel.percpu.cpu.user)"; +ncpu = "hinv.ncpu"; + +// average CPU utilization +summary.cpu.util = avg_inst $cpuse; + +// proportion of CPUs that are busy +summary.cpu.busy = (count_inst $cpuse > 0.7) / $ncpu; + +// Disk utilization +// +diskio = "disk.dev.total"; +ndisk = "hinv.ndisk"; + +// average spindle activity +summary.disk.iops = avg_inst ($diskio); + +// proportion of disk spindles that are busy +summary.disk.busy = (count_inst $diskio > 40) / $ndisk; + +// Network interface utilization +// +netio = "network.interface.total.packets"; + +// average network interface activity +summary.netif.packets = avg_inst ($netio); + +// proportion of network interfaces that are busy +summary.netif.busy = (count_inst $netio > 400) / (count_inst $netio >= 0); diff --git a/src/pmdas/systemd/GNUmakefile b/src/pmdas/systemd/GNUmakefile new file mode 100644 index 0000000..9805c1d --- /dev/null +++ b/src/pmdas/systemd/GNUmakefile @@ -0,0 +1,59 @@ +# +# Copyright (c) 2012 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +CMDTARGET = pmdasystemd$(EXECSUFFIX) +DFILES = README +CFILES = systemd.c +LCFLAGS = $(SYSTEMD_CFLAGS) +LLDLIBS = $(PCP_PMDALIB) $(SYSTEMD_LIBS) +LSRCFILES = Install Remove pmns help $(DFILES) root + +IAM = systemd +DOMAIN = SYSTEMD +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h *.o $(IAM).log $(CMDTARGET) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(PMDA_SYSTEMD)" "" +build-me: domain.h $(CMDTARGET) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns $(PMDADIR) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/$(CMDTARGET) +else +build-me: +install: +endif + +systemd.o: domain.h + +.NOTPARALLEL: +.ORDER: domain.h $(OBJECTS) + +default_pcp : default + +install_pcp : install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/systemd/Install b/src/pmdas/systemd/Install new file mode 100755 index 0000000..2757725 --- /dev/null +++ b/src/pmdas/systemd/Install @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2011-2013 Red Hat Inc. +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the systemd PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=systemd +pmda_interface=6 +pipe_opt=true +daemon_opt=true +pmdaSetup +pmdaInstall + +exit 0 diff --git a/src/pmdas/systemd/README b/src/pmdas/systemd/README new file mode 100644 index 0000000..3125da1 --- /dev/null +++ b/src/pmdas/systemd/README @@ -0,0 +1,64 @@ +Systemd PMDA +=========== + +This PMDA exports events from the systemd journal [1] as they occur. +There is no configuration required. In the default (daemon) PMDA +mode, the daemon switches to userid 'adm', so it can report systemwide +journal entries. + +[1] http://www.freedesktop.org/wiki/Software/systemd + + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT systemd + +The interesting metrics are systemd.journal.records and +systemd.journal.records_raw. When a PCP client such as pmevent +monitors them, each new journal entry is reported as a PCP event +tuple. Each field of the journal entry is transcribed as a string or +blob, including the FIELDNAME= prefix. See [2] for more details about +interpretation of the fields. + +[2] http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html + + +Installation +============ + + + # cd $PCP_PMDAS_DIR/systemd + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/systemd + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/systemd.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/systemd/Remove b/src/pmdas/systemd/Remove new file mode 100755 index 0000000..00e48aa --- /dev/null +++ b/src/pmdas/systemd/Remove @@ -0,0 +1,24 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2011-2012 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the systemd PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh +iam=systemd +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/systemd/help b/src/pmdas/systemd/help new file mode 100644 index 0000000..1a40f7a --- /dev/null +++ b/src/pmdas/systemd/help @@ -0,0 +1,52 @@ +# +# Copyright (c) 2012 Red Hat, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# systemd PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ systemd.numclients The number of attached clients +The number of attached clients. +@ systemd.maxmem Maximum number of queued event bytes. +Maximum number of queued event bytes (apprx. 128 bytes per cursor string). + +@ systemd.journal.field.cursor The cursor, an implicit journald field. +This is the journal entry's permanent, globally unique cursor string. +@ systemd.journal.field.string A journal field that may be a string. +A journal field copied verbatim, as a PM_TYPE_STRING object, presumed as +a valid string (in some encoding), if the field did not contain any \0 characters. +@ systemd.journal.field.blob A journal field copied verbatim. +A journal field copied verbatim, as a PM_TYPE_AGGREGATE object. + +@ systemd.journal.records Journal entries, encoded as strings and blobs. +Each new journald event field is given a systemd.parameters.cursor string +to identify it, and a collection of string and blob fields (as appropriate). + +@ systemd.journal.records_raw Journal entries, encoded as blob parameters only. +Each new journald event field is given a systemd.parameters.cursor string +to identify it, and a blob fields the reproduce the FIELD=value bit-for-bit. + +@ systemd.journal.count Count of journal entries observed +@ systemd.journal.bytes Sum of sizes of all journal entries observed diff --git a/src/pmdas/systemd/pmns b/src/pmdas/systemd/pmns new file mode 100644 index 0000000..b9e6487 --- /dev/null +++ b/src/pmdas/systemd/pmns @@ -0,0 +1,35 @@ +/* + * Metrics for systemd PMDA + * + * Copyright (c) 2012-2013 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +systemd { + numclients SYSTEMD:0:0 + maxmem SYSTEMD:0:1 + journal +} + +systemd.journal { + records SYSTEMD:2:0 + records_raw SYSTEMD:2:1 + field + count SYSTEMD:2:2 + bytes SYSTEMD:2:3 +} + +systemd.journal.field { + cursor SYSTEMD:1:0 + string SYSTEMD:1:1 /* possibly a string */ + blob SYSTEMD:1:2 /* a binary blob */ +} diff --git a/src/pmdas/systemd/root b/src/pmdas/systemd/root new file mode 100644 index 0000000..cac7f3a --- /dev/null +++ b/src/pmdas/systemd/root @@ -0,0 +1,11 @@ +#include + +root { + systemd +} + +#ifndef SYSTEMD +#define SYSTEMD 114 +#endif + +#include "pmns" diff --git a/src/pmdas/systemd/systemd.c b/src/pmdas/systemd/systemd.c new file mode 100644 index 0000000..c67cb66 --- /dev/null +++ b/src/pmdas/systemd/systemd.c @@ -0,0 +1,791 @@ +/* + * systemd support for the systemd PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * Structure based upon the logger pmda. + */ + +#define _POSIX_C_SOURCE 200112L /* for strtoull */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_MAXMEM (2 * 1024 * 1024) /* 2 megabytes */ +long maxmem; +int maxfd; +fd_set fds; +static int interval_expired; +static struct timeval interval = { 60, 0 }; +static sd_journal *journald_context; /* Used for monitoring only. */ +static sd_journal *journald_context_seeky; /* Used for event detail extraction, + involving seeks. */ +static int queue_entries = -1; +static char *username = "adm"; + + +/* Track per-context PCP_ATTR_USERID | _GROUPID, so we + can filter event records for that context. */ +static int uid_gid_filter_p = 1; +struct uid_gid_tuple { + char wildcard_p; /* do not filter for this context. */ + char uid_p; char gid_p; /* uid/gid received flags. */ + int uid; int gid; }; /* uid/gid received from PCP_ATTR_* */ +static struct uid_gid_tuple *ctxtab = NULL; +int ctxtab_size = 0; + + +static pmdaMetric metrictab[] = { +/* numclients */ +#define METRICTAB_NUMCLIENTS_PMID metrictab[0].m_desc.pmid + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* maxmem */ +#define METRICTAB_MAXMEM_PMID metrictab[1].m_desc.pmid + { NULL, + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +/* journal.field.cursor */ +#define METRICTAB_JOURNAL_CURSOR_PMID metrictab[2].m_desc.pmid + { NULL, + { PMDA_PMID(1,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* journal.field.string */ +#define METRICTAB_JOURNAL_STRING_PMID metrictab[3].m_desc.pmid + { NULL, + { PMDA_PMID(1,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* journal.field.blob */ +#define METRICTAB_JOURNAL_BLOB_PMID metrictab[4].m_desc.pmid + { NULL, + { PMDA_PMID(1,2), PM_TYPE_AGGREGATE, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* journal.records */ +#define METRICTAB_JOURNAL_RECORDS_PMID metrictab[5].m_desc.pmid + { NULL, + { PMDA_PMID(2,0), PM_TYPE_EVENT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* journal.records_raw */ +#define METRICTAB_JOURNAL_RECORDS_RAW_PMID metrictab[6].m_desc.pmid + { NULL, + { PMDA_PMID(2,1), PM_TYPE_EVENT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* journal.count */ +#define METRICTAB_JOURNAL_COUNT_PMID metrictab[7].m_desc.pmid + { NULL, + { PMDA_PMID(2,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, +/* journal.bytes */ +#define METRICTAB_JOURNAL_BYTES_PMID metrictab[8].m_desc.pmid + { NULL, + { PMDA_PMID(2,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, }, +}; + + +void systemd_shutdown(void) +{ + if (journald_context != 0) + sd_journal_close (journald_context); + + if (journald_context_seeky != 0) + sd_journal_close (journald_context_seeky); + + /* XXX: pmdaEvent zap queues? */ +} + + +/* Return a strndup (or NULL) of a field of the current journal entry, + since sd_journal_get_data returns data that is not + \0-terminated. */ +char * +my_sd_journal_get_data(sd_journal *j, const char *field) +{ + int rc; + const char* str; + size_t str_len; + + assert (j != NULL); + assert (field != NULL); + + rc = sd_journal_get_data(j, field, + (const void**) & str, & str_len); + if (rc < 0) + return NULL; + + return strndup (str, str_len); +} + + +void systemd_refresh(void) +{ + /* Absorb any changes such as inotify() messages. */ + (void) sd_journal_process(journald_context); + (void) sd_journal_process(journald_context_seeky); + + while (1) { + char *cursor = NULL; + char *timestamp_str = NULL; + struct timeval timestamp; + + int rc = sd_journal_next(journald_context); + + if (rc == 0) /* No recent entries. */ + break; + + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_next failure: %s", strerror(-rc)); + break; + } + + /* NB: we enqueue the journal cursor string, rather than the + actual journal records. */ + rc = sd_journal_get_cursor(journald_context, &cursor); + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_get_cursor failure: %s", + strerror(-rc)); + break; + } + + /* Extract a timestamp from the journald event fields. */ + timestamp_str = my_sd_journal_get_data(journald_context, + "_SOURCE_REALTIME_TIMESTAMP"); + if (timestamp_str == NULL) + timestamp_str = my_sd_journal_get_data(journald_context, + "__REALTIME_TIMESTAMP"); + if (timestamp_str == NULL) + rc = -ENOMEM; + else { + const char* curse; + unsigned long long epoch_us; + /* defined in systemd.journal-fields(7) as + FIELD_NAME=NNNN, where NNNN is decimal us since epoch. */ + curse = strchr (timestamp_str, '='); + if (curse == NULL) + rc = -EINVAL; + else { + curse ++; + epoch_us = strtoull (curse, NULL, 10); + timestamp.tv_sec = epoch_us / 1000000; + timestamp.tv_usec = epoch_us % 1000000; + } + free (timestamp_str); + } + /* Improvise. */ + if (rc < 0) + gettimeofday (& timestamp, NULL); + + /* Enqueue it to fresh visitors. */ + rc = pmdaEventQueueAppend(queue_entries, + cursor, strlen(cursor)+1 /* \0 */, ×tamp); + free(cursor); /* Already copied. */ + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "pmdaEventQueueAppend failure: %s", pmErrStr(rc)); + break; + } + } +} + + + +enum journald_field_encoding { + JFE_STRING_BLOB_AUTO, + JFE_BLOB_ONLY +}; + + + +int +systemd_journal_event_filter (void *rp, void *data, size_t size) +{ + int rc; + struct uid_gid_tuple* ugt = rp; + + assert (ugt == & ctxtab[pmdaGetContext()]); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "filter (%d) uid=%d gid=%d data=%p bytes=%u\n", + pmdaGetContext(), ugt->uid, ugt->gid, data, (unsigned)size); + + /* The data/size pair gives the object in the event queue, i.e., + the systemd journal cursor string. It has not yet been turned + into a PM_TYPE_EVENT tuple yet, and if we have our way, it won't + be (for non-participating clients). */ + + /* The general filtering idea is to only feed journal records to clients + if their uid matches the _UID=NNN field -or- gid matches _GID=MMM + -or- the client is highly authenticated (wildcard_p) -or- per-uid filtering + was turned off at the pmda level. */ + + /* Reminder: function rc == 0 passes the filter. */ + + /* Unfiltered? Everyone gets egg soup! */ + if (! uid_gid_filter_p) + return 0; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "filter (%d) uid%s%d gid%s%d wildcard=%d\n", + pmdaGetContext(), + ugt->uid_p?"=":"?", ugt->uid, + ugt->gid_p?"=":"?", ugt->gid, + ugt->wildcard_p); + + /* Superuser? May we offer some goulash? */ + if (ugt->wildcard_p) + return 0; + + /* Unauthenticated context? No soup for you! */ + if (! ugt->uid_p && ! ugt->gid_p) + return 1; + + /* OK, we need to take a look at the journal record in question. */ + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "filter cursor=%s\n", (const char*) data); + + (void) size; /* already known \0-terminated */ + rc = sd_journal_seek_cursor(journald_context_seeky, (char*) data); + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "filter cannot seek to cursor=%s\n", + (const char*) data); + return 1; /* No point trying again in systemd_journal_decoder. */ + } + + rc = sd_journal_next(journald_context_seeky); + if (rc < 0) { + __pmNotifyErr(LOG_ERR, "filter cannot advance to next\n"); + return 1; /* No point trying again in systemd_journal_decoder. */ + } + + if (ugt->uid_p) { + char *uid_str = my_sd_journal_get_data(journald_context_seeky, "_UID"); + if (uid_str) { + int uid = atoi (& uid_str[5]); /* skip over _UID= */ + free (uid_str); + if (uid == ugt->uid) + return 0; /* You're a somebody. Here's a bowl of stew. */ + } + } + + if (ugt->gid_p) { + char *gid_str = my_sd_journal_get_data(journald_context_seeky, "_GID"); + if (gid_str) { + int gid = atoi (& gid_str[5]); /* skip over _GID= */ + free (gid_str); + if (gid == ugt->gid) + return 0; /* You're with pals. Here's a bowl of miso. */ + } + } + + /* No soup for you! */ + return 1; +} + + +void +systemd_journal_event_filter_release (void *rp) +{ + /* NB: We have nothing to release, as we don't do memory allocation + for the filter per se - we clean up during end-context time. + We can't send a NULL to pmdaEventSetFilter for release purposes + (since it'll blindly call it), so need this dummy function. */ + + (void) rp; +} + + +int +systemd_journal_decoder(int eventarray, void *buffer, size_t size, + struct timeval *timestamp, void *data) +{ + int sts; + pmAtomValue atom; + enum journald_field_encoding jfe = * (enum journald_field_encoding *) data; + + sts = pmdaEventAddRecord(eventarray, timestamp, PM_EVENT_FLAG_POINT); + if (sts < 0) + return sts; + + /* Go to the cursor point enqueued for this client. The buffer is already + \0-terminated. */ + sts = sd_journal_seek_cursor(journald_context_seeky, (char*) buffer); + if (sts < 0) { + /* But see RHBZ #876654. */ + return /* sts */ 0; + } + + sts = sd_journal_next(journald_context_seeky); + if (sts < 0) + return sts; + if (sts == 0) + return -ENODATA; /* event got lost between cursor-recording and now */ + + /* Add the _CURSOR implicit journal field. */ + atom.cp = buffer; + sts = pmdaEventAddParam(eventarray, METRICTAB_JOURNAL_CURSOR_PMID, + PM_TYPE_STRING, &atom); + + /* Add all the explicit journal fields. */ + while (1) { + const void *data; + size_t data_len; + + if (sts < 0) + break; + sts = sd_journal_enumerate_data(journald_context_seeky, &data, &data_len); + if (sts <= 0) + break; + + /* Infer string upon absence of embedded \0's. */ + if (jfe == JFE_STRING_BLOB_AUTO && (memchr (data, '\0', data_len) == NULL)) { + /* Unfortunately, data may not be \0-terminated, so we can't simply pass + it to atom.cp. We need to copy the bad boy first. */ + atom.cp = strndup(data, data_len); + if (atom.cp == NULL) + sts = -ENOMEM; + else { + sts = pmdaEventAddParam(eventarray, METRICTAB_JOURNAL_STRING_PMID, + PM_TYPE_STRING, &atom); + free (atom.cp); + } + /* NB: we assume libpcp_pmda will not free() the field. */ + } else { + pmValueBlock *aggr = (pmValueBlock *)malloc(PM_VAL_HDR_SIZE + data_len); + if (aggr == NULL) + sts = -ENOMEM; + else { + aggr->vtype = PM_TYPE_AGGREGATE; + if (PM_VAL_HDR_SIZE + data_len >= 1<<24) + aggr->vlen = (1U<<24) - 1; /* vlen is a :24 bit field */ + else + aggr->vlen = PM_VAL_HDR_SIZE + data_len; + memcpy (aggr->vbuf, data, data_len); + atom.vbp = aggr; + sts = pmdaEventAddParam(eventarray, METRICTAB_JOURNAL_BLOB_PMID, + PM_TYPE_AGGREGATE, &atom); + /* NB: we assume libpcp_pmda will free() aggr. */ + } + } + } + + return sts < 0 ? sts : 1; /* added one event array */ +} + + +void enlarge_ctxtab(int context) +{ + /* Grow the context table if necessary. */ + if (ctxtab_size /* cardinal */ <= context /* ordinal */) { + size_t need = (context + 1) * sizeof(struct uid_gid_tuple); + ctxtab = realloc (ctxtab, need); + if (ctxtab == NULL) + __pmNoMem("systemd ctx table", need, PM_FATAL_ERR); + /* Blank out new entries. */ + while (ctxtab_size <= context) + memset (& ctxtab[ctxtab_size++], 0, sizeof(struct uid_gid_tuple)); + } +} + + +static int +systemd_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int sts; + (void) pmdaEventNewClient(pmda->e_context); + enlarge_ctxtab(pmda->e_context); + sts = pmdaEventSetFilter(pmda->e_context, queue_entries, + & ctxtab[pmda->e_context], /* any non-NULL value */ + systemd_journal_event_filter, + systemd_journal_event_filter_release /* NULL */); + if (sts < 0) + return sts; + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + + +static int +systemd_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + pmID id = mdesc->m_desc.pmid; + int sts; + + if (id == METRICTAB_NUMCLIENTS_PMID) { + sts = pmdaEventClients(atom); + } else if (id == METRICTAB_MAXMEM_PMID) { + atom->ul = (unsigned long)maxmem; + sts = PMDA_FETCH_STATIC; + } else if (id == METRICTAB_JOURNAL_CURSOR_PMID) { + sts = PMDA_FETCH_NOVALUES; + } else if (id == METRICTAB_JOURNAL_STRING_PMID) { + sts = PMDA_FETCH_NOVALUES; + } else if (id == METRICTAB_JOURNAL_BLOB_PMID) { + sts = PMDA_FETCH_NOVALUES; + } else if (id == METRICTAB_JOURNAL_COUNT_PMID) { + sts = pmdaEventQueueCounter(queue_entries, atom); + } else if (id == METRICTAB_JOURNAL_BYTES_PMID) { + sts = pmdaEventQueueBytes(queue_entries, atom); + } else if (id == METRICTAB_JOURNAL_RECORDS_PMID) { + enum journald_field_encoding jfe = JFE_STRING_BLOB_AUTO; + sts = pmdaEventSetAccess(pmdaGetContext(), queue_entries, 1); + if (sts == 0) + sts = pmdaEventQueueRecords(queue_entries, atom, pmdaGetContext(), + systemd_journal_decoder, & jfe); + } else if (id == METRICTAB_JOURNAL_RECORDS_RAW_PMID) { + enum journald_field_encoding jfe = JFE_BLOB_ONLY; + sts = pmdaEventSetAccess(pmdaGetContext(), queue_entries, 1); + if (sts == 0) + sts = pmdaEventQueueRecords(queue_entries, atom, pmdaGetContext(), + systemd_journal_decoder, & jfe); + } else { + sts = PM_ERR_PMID; + } + return sts; +} + + +static int +systemd_contextAttributeCallBack(int context, + int attr, const char *value, int length, pmdaExt *pmda) +{ + static int rootlike_gids_found = 0; + static int adm_gid = -1; + static int wheel_gid = -1; + static int systemd_journal_gid = -1; + int id; + + /* Look up root-like gids if needed. A later PCP client that + matches any of these group-id's is treated as if root/adm, + i.e., journal records are not filtered for them (wildcard_p). + XXX: we could examine group-membership lists and check against + uid to also set wildcard_p. */ + if (! rootlike_gids_found) { + struct group *grp; + grp = getgrnam("adm"); + if (grp) adm_gid = grp->gr_gid; + grp = getgrnam("wheel"); + if (grp) wheel_gid = grp->gr_gid; + grp = getgrnam("systemd-journal"); + if (grp) systemd_journal_gid = grp->gr_gid; + rootlike_gids_found = 1; + } + + enlarge_ctxtab(context); + assert (ctxtab != NULL && context < ctxtab_size); + + /* NB: we maintain separate uid_p and gid_p for filtering + purposes; it's possible that a pcp client might send only + PCP_ATTR_USERID, leaving gid=0, possibly leading us to + misinterpret that as GROUPID=0 (root) and sending back _GID=0 + records. */ + switch (attr) { + case PCP_ATTR_USERID: + ctxtab[context].uid_p = 1; + id = atoi(value); + ctxtab[context].uid = id; + if (id == 0) /* root */ + ctxtab[context].wildcard_p = 1; + break; + + case PCP_ATTR_GROUPID: + ctxtab[context].gid_p = 1; + id = atoi(value); + ctxtab[context].gid = id; + if (id == adm_gid || + id == wheel_gid || + id == systemd_journal_gid) + ctxtab[context].wildcard_p = 1; + break; + } + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "attrib (%d) uid%s%d gid%s%d wildcard=%d\n", + context, + ctxtab[context].uid_p?"=":"?", ctxtab[context].uid, + ctxtab[context].gid_p?"=":"?", ctxtab[context].gid, + ctxtab[context].wildcard_p); + + return 0; +} + + +static void +systemd_end_contextCallBack(int context) +{ + pmdaEventEndClient(context); + + /* assert (ctxtab != NULL && context < ctxtab_size); */ + + /* NB: don't do that; this callback may be hit without any fetch + calls having been performed, this ctxtab not stretching all the + way to [context]. */ + + if (context < ctxtab_size) + memset (& ctxtab[context], 0, sizeof(struct uid_gid_tuple)); +} + + +static int +systemd_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda) +{ + return pmdaDesc(pmid, desc, pmda); +} + + +static int +systemd_text(int ident, int type, char **buffer, pmdaExt *pmda) +{ + return pmdaText(ident, type, buffer, pmda); +} + + +void +systemd_init(pmdaInterface *dp) +{ + int sts; + int journal_fd; + + dp->version.six.desc = systemd_desc; + dp->version.six.fetch = systemd_fetch; + dp->version.six.text = systemd_text; + dp->version.six.attribute = systemd_contextAttributeCallBack; + pmdaSetFetchCallBack(dp, systemd_fetchCallBack); + pmdaSetEndContextCallBack(dp, systemd_end_contextCallBack); + pmdaInit(dp, NULL, 0, metrictab, sizeof(metrictab)/sizeof(metrictab[0])); + + /* Initialize the systemd side. This is failure-tolerant. */ + /* XXX: SD_JOURNAL_{LOCAL|RUNTIME|SYSTEM}_ONLY */ + sts = sd_journal_open(& journald_context, 0); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_open failure: %s", + strerror(-sts)); + dp->status = sts; + return; + } + + sts = sd_journal_open(& journald_context_seeky, 0); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_open #2 failure: %s", + strerror(-sts)); + dp->status = sts; + return; + } + + sts = sd_journal_seek_tail(journald_context); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_seek_tail failure: %s", + strerror(-sts)); + } + + /* Work around RHBZ979487. */ + sts = sd_journal_previous_skip(journald_context, 1); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_previous_skip failure: %s", + strerror(-sts)); + } + + /* Arrange to wake up for journal events. */ + journal_fd = sd_journal_get_fd(journald_context); + if (journal_fd < 0) { + __pmNotifyErr(LOG_ERR, "sd_journal_get_fd failure: %s", + strerror(-journal_fd)); + /* NB: not a fatal error; the select() loop will still time out and + periodically poll. This makes it ok for sd_journal_reliable_fd() + to be 0. */ + } else { + FD_SET(journal_fd, &fds); + if (journal_fd > maxfd) maxfd = journal_fd; + } + + /* NB: One queue is used for both .records and .records_raw; they + just use different decoder callbacks. */ + queue_entries = pmdaEventNewQueue("systemd", maxmem); + if (queue_entries < 0) + __pmNotifyErr(LOG_ERR, "pmdaEventNewQueue failure: %s", + pmErrStr(queue_entries)); +} + + +void +systemdMain(pmdaInterface *dispatch) +{ + int pmcdfd; + + pmcdfd = __pmdaInFd(dispatch); + if (pmcdfd > maxfd) + maxfd = pmcdfd; + + FD_SET(pmcdfd, &fds); + + for (;;) { + fd_set readyfds; + int nready; + struct timeval select_timeout = interval; + + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, & select_timeout); + if (pmDebug & DBG_TRACE_APPL2) + __pmNotifyErr(LOG_DEBUG, "select: nready=%d interval=%d", + nready, interval_expired); + if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } else if (!interval_expired) { + continue; + } + } + + if (nready > 0 && FD_ISSET(pmcdfd, &readyfds)) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd PDU [fd=%d]", pmcdfd); + if (__pmdaMainPDU(dispatch) < 0) { + exit(1); /* fatal if we lose pmcd */ + } + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "completed pmcd PDU [fd=%d]", pmcdfd); + } + systemd_refresh(); + } +} + + +static void +convertUnits(char **endnum, long *maxmem) +{ + switch ((int) **endnum) { + case 'b': + case 'B': + break; + case 'k': + case 'K': + *maxmem *= 1024; + break; + case 'm': + case 'M': + *maxmem *= 1024 * 1024; + break; + case 'g': + case 'G': + *maxmem *= 1024 * 1024 * 1024; + break; + } + (*endnum)++; +} + + +static void +usage(void) +{ + fprintf(stderr, + "Usage: %s [options]\n\n" + "Options:\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -m memory maximum memory used per queue (default %ld bytes)\n" + " -s interval default delay between iterations (default %d sec)\n" + " -U username user account to run under (default \"adm\")\n" + " -f disable per-uid/gid record filtering (default on)\n", + pmProgname, maxmem, (int)interval.tv_sec); + exit(1); +} + + +int +main(int argc, char **argv) +{ + static char helppath[MAXPATHLEN]; + char *endnum; + pmdaInterface desc; + long minmem; + int c, err = 0, sep = __pmPathSeparator(); + + minmem = getpagesize(); + maxmem = (minmem > DEFAULT_MAXMEM) ? minmem : DEFAULT_MAXMEM; + __pmSetProgname(argv[0]); + snprintf(helppath, sizeof(helppath), "%s%c" "systemd" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_6, pmProgname, SYSTEMD, + "systemd.log", helppath); + + while ((c = pmdaGetOpt(argc, argv, "D:d:l:m:s:U:f?", &desc, &err)) != EOF) { + switch (c) { + case 'm': + maxmem = strtol(optarg, &endnum, 10); + if (*endnum != '\0') + convertUnits(&endnum, &maxmem); + if (*endnum != '\0' || maxmem < minmem) { + fprintf(stderr, "%s: invalid max memory '%s' (min=%ld)\n", + pmProgname, optarg, minmem); + err++; + } + break; + + case 's': + if (pmParseInterval(optarg, &interval, &endnum) < 0) { + fprintf(stderr, "%s: -s requires a time interval: %s\n", + pmProgname, endnum); + free(endnum); + err++; + } + break; + + case 'U': + username = optarg; + break; + + case 'f': + uid_gid_filter_p = 0; + break; + + default: + err++; + break; + } + } + + if (err) + usage(); + + FD_ZERO (&fds); + pmdaOpenLog(&desc); + + /* The systemwide journal may be accessed by the adm user (group); + root access is not necessary. */ + __pmSetProcessIdentity(username); + desc.comm.flags |= PDU_FLAG_AUTH; + pmdaConnect(&desc); + // After this point, systemd_init is allowed to take some extra time. + systemd_init(&desc); // sets some fds + systemdMain(&desc); // sets some more fds + systemd_shutdown(); + exit(0); +} + +/* + Local Variables: + c-basic-offset: 4 + End: +*/ diff --git a/src/pmdas/systemtap/GNUmakefile b/src/pmdas/systemtap/GNUmakefile new file mode 100644 index 0000000..ad7d51d --- /dev/null +++ b/src/pmdas/systemtap/GNUmakefile @@ -0,0 +1,56 @@ +#!gmake +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = systemtap +DOMAIN = SYSTEMTAP +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl probes.stp + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default +ifeq "$(TARGET_OS)" "linux" + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 probes.stp pmda$(IAM).pl $(PMDADIR) + @$(INSTALL_MAN) +endif + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/systemtap/Install b/src/pmdas/systemtap/Install new file mode 100755 index 0000000..bf6dce4 --- /dev/null +++ b/src/pmdas/systemtap/Install @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the SystemTap PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=systemtap +perl_opt=true +daemon_opt=false +forced_restart=false + +if ! test -x /usr/bin/stap; then + echo "SystemTap \"stap\" tool is not installed" && exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/systemtap/README b/src/pmdas/systemtap/README new file mode 100644 index 0000000..ddb92c4 --- /dev/null +++ b/src/pmdas/systemtap/README @@ -0,0 +1,59 @@ +SystemTap PMDA +============== + +This PMDA uses the SystemTap Linux kernel trace infrastructure to obtain +performance data. Both SystemTap and this PMDA are easily configurable, +allowing arbitrary trace points to be monitored. The PMDA itself is all +Perl code, and thus easily extended to monitor additional types of trace +information from SystemTap. + +The file $PCP_PMDAS_DIR/probes.stp contains the SystemTap script which +will be run by the stap(1) command to insert the kernel instrumentation. +It is intended that once suitable instrumentation has been found running +SystemTap interactively, that a modified probes.stp and PMDA would then +be installed to export the interesting data to PCP clients (for logging, +charting, etc). + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT systemtap + +Installation +============ + + + # cd $PCP_PMDAS_DIR/systemtap + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit pmdasystemtap.pl to use + a different domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/systemtap + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/systemtap.log) should be checked for any + warnings or errors. diff --git a/src/pmdas/systemtap/Remove b/src/pmdas/systemtap/Remove new file mode 100755 index 0000000..6b25342 --- /dev/null +++ b/src/pmdas/systemtap/Remove @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the SystemTap PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=systemtap + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/systemtap/pmdasystemtap.pl b/src/pmdas/systemtap/pmdasystemtap.pl new file mode 100644 index 0000000..801b788 --- /dev/null +++ b/src/pmdas/systemtap/pmdasystemtap.pl @@ -0,0 +1,168 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +use vars qw( $pmda $id ); +my $probe_indom = 0; +my $probe_script = pmda_config('PCP_PMDAS_DIR') . '/systemtap/probes.stp'; +my $probe_command = "/usr/bin/stap -m pmdasystemtap $probe_script"; +my @probe_instances = ( 0 => 'sync', 1 => 'readdir' ); +my ( $sync_count, $sync_pid, $sync_cmd ) = ( 0, 0, "(none)" ); +my ( $readdir_count, $readdir_pid, $readdir_cmd ) = ( 0, 0, "(none)" ); + +sub systemtap_input_callback +{ + ( $id, $_ ) = @_; + # $pmda->log($_); + + if (/^readdir: \((\d+)\) (.*)$/) { + ( $readdir_pid, $readdir_cmd ) = ( $1, $2 ); + $readdir_count++; + } + elsif (/^sync: \((\d+)\) (.*)$/) { + ( $sync_pid, $sync_cmd ) = ( $1, $2 ); + $sync_count++; + } +} + +sub systemtap_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + + if ($inst < 0 || $inst > 1) { return (PM_ERR_INST, 0); } + if ($cluster == 0) { + if ($item == 0) { + if ($inst == 0) { return ($sync_count, 1); } + else { return ($readdir_count, 1); } + } + elsif ($item == 1) { + if ($inst == 0) { return ($sync_pid, 1); } + else { return ($readdir_pid, 1); } + } + elsif ($item == 2) { + if ($inst == 0) { return ($sync_cmd, 1); } + else { return ($readdir_cmd, 1); } + } + } + return (PM_ERR_PMID, 0); +} + +$pmda = PCP::PMDA->new('systemtap', 88); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, $probe_indom, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'systemtap.probes.count', + 'Number of times the probe has been observed', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_32, $probe_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'systemtap.probes.pid', + 'The PID of the last process to pass the probe point', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_STRING, $probe_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'systemtap.probes.cmd', + 'The name of the last process to pass the probe point', ''); + +$pmda->add_indom($probe_indom, \@probe_instances, + 'Instance domain exporting each SystemTap probe', ''); + +$pmda->set_fetch_callback(\&systemtap_fetch_callback); +$pmda->add_pipe($probe_command, \&systemtap_input_callback, 0); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdasystemtap - Systemtap performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from the Linux Systemtap dynamic tracing toolkit. + +This implementation uses the stap(1) tool, which is a front-end to +the Systemtap toolkit. + +=head1 INSTALLATION + +In order to access performance data exported by Systemtap from +with PCP, it is necessary to perform two configuration steps: + +=over + +=item 1. + +Configure Systemtap probes, and verify them with stap(1). +These should be produced in a format that is easily parsed, +and then stored in the $PCP_PMDAS_DIR/systemtap/probes.stp +file. + +=item 2. + +Configure B to extract the values from the text +produced by stap. Two example probes are implemented in the +default systemtap PMDA script - readdir and sync traces (see +$PCP_PMDAS_DIR/systemtap/pmdasystemtap.pl for details). + +=back + + # cd $PCP_PMDAS_DIR/systemtap + # [ edit probes.stp, test /usr/bin/stap probes.stp ] + # [ edit pmdasystemtap.pl ] + +Once this is setup, you can access the names and values for the +systemtap performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/systemtap + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/systemtap + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/systemtap/probes.stp + +probe configuration file for stap(1), run by B + +=item $PCP_PMDAS_DIR/systemtap/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/systemtap/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/systemtap.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1) and stap(1). diff --git a/src/pmdas/systemtap/probes.stp b/src/pmdas/systemtap/probes.stp new file mode 100644 index 0000000..fbb1923 --- /dev/null +++ b/src/pmdas/systemtap/probes.stp @@ -0,0 +1,6 @@ +probe kernel.function("vfs_readdir") { + printf ("readdir: (%d) %s\n", pid(), execname()) +} +probe kernel.function("sys_sync") { + printf ("sync: (%d) %s\n", pid(), execname()) +} diff --git a/src/pmdas/trace/GNUmakefile b/src/pmdas/trace/GNUmakefile new file mode 100644 index 0000000..64d2512 --- /dev/null +++ b/src/pmdas/trace/GNUmakefile @@ -0,0 +1,109 @@ +# +# Copyright (c) 2000-2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = trace +DOMAIN = TRACE + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +DEMODIR = $(PCP_DEMOS_DIR)/$(IAM) + +SCRIPTS = Install Remove +OTHERS = pmns root +DFILES = help README +DEMOS = app1.c app2.c app3.c fapp1.f japp1.java +DEMOFILES = README.demos Makefile.proto GNUmakefile.stub stub.c +APPS = app1$(EXECSUFFIX) app2$(EXECSUFFIX) app3$(EXECSUFFIX) + +LCFLAGS = -I. +LLDFLAGS= -L$(TOPDIR)/src/libpcp/src -L$(TOPDIR)/src/libpcp_trace/src +LLDLIBS = $(PCP_TRACELIB) + +LDIRT = *.log *.dir *.pag *.o $(APPS) tmp.c \ + pmtrace.c Makefile.demos + +LSRCFILES= $(SCRIPTS) $(OTHERS) $(DEMOS) $(DEMOFILES) $(DFILES) + +SUBDIRS = src + +default: $(SUBDIRS) Makefile.demos build-me + $(SUBDIRS_MAKERULE) + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: demos + +install: $(SUBDIRS) Makefile.demos + $(SUBDIRS_MAKERULE) + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(OTHERS) $(DFILES) $(PMDADIR) + $(INSTALL) -m 755 -d $(DEMODIR) + $(INSTALL) -m 644 Makefile.demos $(DEMODIR)/Makefile + $(INSTALL) -m 644 README.demos $(DEMODIR)/README + $(INSTALL) -m 644 GNUmakefile.stub $(DEMODIR)/Makefile.stub + $(INSTALL) -m 644 stub.c pmtrace.c $(DEMOS) $(DEMODIR) + +else +build-me: +install: $(SUBDIRS) +endif + +demos: $(APPS) pmtrace.c + +pmtrace.c: $(TOPDIR)/src/pmtrace/pmtrace.c + rm -f $@ && cp $< $@ + +app1.o: app1.c $(TOPDIR)/src/include/pcp/pmapi.h + @rm -f tmp.c + sed -e 's;;"\1";' app1.c >tmp.c + $(CCF) -c -o $@ tmp.c + @rm -f tmp.c + +app2.o: app2.c $(TOPDIR)/src/include/pcp/pmapi.h + @rm -f tmp.c + sed -e 's;;"\1";' app2.c >tmp.c + $(CCF) -c -o $@ tmp.c + @rm -f tmp.c + +app3.o: app3.c $(TOPDIR)/src/include/pcp/pmapi.h + @rm -f tmp.c + sed -e 's;;"\1";' app3.c >tmp.c + $(CCF) -c -o $@ tmp.c + @rm -f tmp.c + +app1$(EXECSUFFIX): app1.o + $(CCF) -o $@ $(LDFLAGS) app1.o $(LDLIBS) + +app2$(EXECSUFFIX): app2.o + $(CCF) -o $@ $(LDFLAGS) app2.o $(LDLIBS) + +app3$(EXECSUFFIX): app3.o + $(CCF) -o $@ $(LDFLAGS) app3.o $(LDLIBS) $(LIB_FOR_PTHREADS) + +default_pcp: default + +install_pcp: install + +.NOTPARALLEL: +Makefile.demos: Makefile.proto + rm -f $@ + sed \ + -e 's/PTHREAD_LIB/$(LIB_FOR_PTHREADS)/' \ + -e 's/DLOPEN_LIB/$(LIB_FOR_DLOPEN)/' \ + -e 's/MATH_LIB/$(LIB_FOR_MATH)/' \ + <$^ >$@ diff --git a/src/pmdas/trace/GNUmakefile.stub b/src/pmdas/trace/GNUmakefile.stub new file mode 100644 index 0000000..8ac95ae --- /dev/null +++ b/src/pmdas/trace/GNUmakefile.stub @@ -0,0 +1,69 @@ +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +include $(PCP_ETC_DIR)/pcp.conf + +# need to deal with these ... +# ELF style +# /usr/lib/libpcp_trace.a /usr/lib/libpcp_trace.so /usr/lib/libpcp_trace.so.2 +# Mac OS X style +# /usr/lib/libpcp_trace.2.dylib +# +DSO_SUFFIX = $(shell ls $(PCP_LIB_DIR)/libpcp_trace.* | sed -e '/\.a$$/d' -e 's/.*libpcp_trace//' -e 's/\.[0-9][0-9]*//' -e 's/^\.//' | sed -e 1q) +ifeq "$(DSO_SUFFIX)" "" +$(error cannot set DSO_SUFFIX on this platform) +endif + +# Note: DSO_VERSION includes (starts with or ends with) $(DSO_SUFFIX) +# +DSO_VERSION = $(shell ls $(PCP_LIB_DIR)/libpcp_trace.* | sed -n -e '/\.a$$/d' -e 's/.*libpcp_trace\.//' -e '/\.$(DSO_SUFFIX)/p' -e '/$(DSO_SUFFIX)\./p' | sed -e 's/^\.//' -e 1q) +ifeq "$(DSO_VERSION)" "" +$(error cannot set DSO_VERSION on this platform) +endif + +SHELL = /bin/sh +CC = cc +TARGETS = lib/libpcp_trace.$(DSO_VERSION) +ifeq "$(shell [ -f $(PCP_LIB_DIR)/libpcp_trace.$(DSO_SUFFIX) ] && echo 1)" "1" +TARGETS += lib/libpcp_trace.$(DSO_SUFFIX) +endif +CFILES = stub.c +CFLAGS += -DPMTRACE_DEBUG +CFLAGS += -fPIC -fno-strict-aliasing +LDIRT = lib lib32 lib64 + +default: $(TARGETS) + +lib/libpcp_trace.$(DSO_VERSION): stub.c + -[ ! -d lib ] && mkdir lib + rm -f $@ + cd lib; $(CC) $(CFLAGS) -shared ../stub.c -o libpcp_trace.$(DSO_VERSION) + +lib/libpcp_trace.$(DSO_SUFFIX): lib/libpcp_trace.$(DSO_VERSION) + rm -f $@ + cd lib; ln -s libpcp_trace.$(DSO_VERSION) libpcp_trace.$(DSO_SUFFIX) + +clean: + rm -rf $(LDIRT) + +clobber: + rm -rf $(LDIRT) $(TARGETS) + +debug: + @echo "DSO_SUFFIX=$(DSO_SUFFIX)" + @echo "DSO_VERSION=$(DSO_VERSION)" + @echo "TARGETS=$(TARGETS)" diff --git a/src/pmdas/trace/Install b/src/pmdas/trace/Install new file mode 100644 index 0000000..f092d4f --- /dev/null +++ b/src/pmdas/trace/Install @@ -0,0 +1,275 @@ +#! /bin/sh +# +# Copyright (c) 1997,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Trace PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=trace +pmda_interface=2 +forced_restart=false + +# Override interactive dialog from pmdaSetup in pmdaproc.sh +# +__choose_mode() +{ + echo "Installing the \"$iam\" Performance Metrics Domain Agent (PMDA) ..." + echo +} + +numeric=0 + +getnumeric() +{ + if [ "X$2" = "X" ] + then + numeric=0 + elif [ "X`expr 0 + $2 2>/dev/null`" != "X$2" ] + then + echo "-- Sorry, $1 must be numeric (not $2) --" + return 1 + else + numeric=$2 + fi + return 0 +} + +getunits() +{ + metric_name=$1 + option_name=$2 + + # Default dimension and scale values + # + dimspace=0 + dimtime=0 + dimcount=0 + scalespace=0 + scaletime=0 + scalecount=0 + + cat - </dev/null + [ $? -ne 1 -a $numeric -ne 0 ] && args="-T $numeric" + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Number of buckets [5]? ""$PCP_ECHO_C" + read value + getnumeric 'Number of buckets' $value >/dev/null + [ $? -ne 1 -a $numeric -ne 0 ] && args="$args -N $numeric" + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Port number for client connections [4323]? ""$PCP_ECHO_C" + read value + getnumeric 'Port number' $value >/dev/null + [ $? -ne 1 -a $numeric -ne 0 ] && args="$args -I $numeric" + + getunits trace.observe.value -U + getunits trace.counter.value -V + + while true + do + access="" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Client host access - (A)llow/(D)isallow [Enter to complete install]? ""$PCP_ECHO_C" + read access + access=`echo $access | tr -d ' '` + [ "X$access" = "X" ] && break + case $access in + A|a|Allow|allow) + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Host specification (IP mask/Enter to cancel): ""$PCP_ECHO_C" + read access + access=`echo $access | tr -d ' '` + if [ "X$access" != "X" ] + then + maxconns="" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Maximum number of connections from $access (Enter for no limit): ""$PCP_ECHO_C" + read maxconns + maxconns=`echo $maxconns | tr -d ' '` + [ "X$maxconns" = "X" ] && maxconns=0 + args="$args"" -A ""allow:$access:$maxconns" + fi;; + D|d|Disallow|disallow) + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Host specification (IP mask/Enter to cancel): ""$PCP_ECHO_C" + read access + access=`echo $access | tr -d ' '` + [ "X$access" != "X" ] && args="$args"" -A ""disallow:$access";; + *) echo 'Try again, "'$access'" not supported.';; + esac + done + echo +fi + +# for debugging the PMDA, uncomment this line ... +# +#args="-D appl0,appl1,pdu $args" + +# Do it ... +# +pmdaInstall +echo Note: some warnings are expected until trace API calls are made - refer to +echo " the man pages for pmtrace(1) and pmdatrace(3) for further details." + +exit 0 diff --git a/src/pmdas/trace/Makefile.proto b/src/pmdas/trace/Makefile.proto new file mode 100644 index 0000000..397035f --- /dev/null +++ b/src/pmdas/trace/Makefile.proto @@ -0,0 +1,69 @@ +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +SHELL = /bin/sh +CC = cc +JC = javac +F77C = f77 +F90C = f90 + +TARGETS = app1 app2 app3 fapp1.f77 fapp1.f90 pmtrace japp1.class + +CDEMOS = app1 app2 app3 pmtrace +F77DEMO = fapp1.f77 +F90DEMO = fapp1.f90 +JDEMO = japp1.class + +CFLAGS = -DPMTRACE_DEBUG -I$(PCP_INC_DIR) +FFLAGS = +JFLAGS = + +default: $(CDEMOS) +fortran77: $(F77DEMO) +fortran90: $(F90DEMO) +java: $(JDEMO) + +pmtrace: pmtrace.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ pmtrace.c -lpcp -lpcp_trace + +app1: app1.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ app1.c -lpcp -lpcp_trace + +app2: app2.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ app2.c -lpcp -lpcp_trace + +app3: app3.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ app3.c -lpcp -lpcp_trace PTHREAD_LIB DLOPEN_LIB MATH_LIB + +fapp1.77: fapp1.f + rm -f $@ + $(F77C) $(FFLAGS) -o $@ fapp1.f -lpcp -lpcp_trace + +fapp1.90: fapp1.f + rm -f $@ + $(F90C) $(FFLAGS) -o $@ fapp1.f -lpcp -lpcp_trace + +japp1.class: japp1.java + rm -f $@ + $(JC) $(JFLAGS) japp1.java + +clean: + rm -f *.o + +clobber: clean + rm -f $(TARGETS) diff --git a/src/pmdas/trace/README b/src/pmdas/trace/README new file mode 100644 index 0000000..0acc1a7 --- /dev/null +++ b/src/pmdas/trace/README @@ -0,0 +1,62 @@ +Trace PMDA +========== + +This PMDA exports application-level transaction and event statistics. + +The PMDA needs to be used in conjunction with the pcp_trace library, +which provides the pmtracebegin, pmtraceend, pmtracepoint, ... functions +to user-level programs. + +For information about the use of the pcp_trace dynamic library and its +function call interface, see pmdatrace(1) and pmdatrace(3). Also, the +example programs - $PCP_VAR_DIR/demos/trace/*.c are useful as starting +points. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT trace + +Installation +============ + + + # cd $PCP_PMDAS_DIR/trace + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + If you choose the "default" installation, appropriate values will + be assigned to those parameters that control the customization of + the PMDA. Otherwise consult the pmdatrace(1) man page for a + description of the customization parameters. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/trace + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/trace.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/trace/README.demos b/src/pmdas/trace/README.demos new file mode 100644 index 0000000..15227a6 --- /dev/null +++ b/src/pmdas/trace/README.demos @@ -0,0 +1,71 @@ +sample pcp_trace applications +============================= + +pmtrace + is a sample application that uses the pcp_trace interface to send +trace data to the trace PMDA (Performance Metrics Domain Agent). + +The binary is shipped as part of pcp and should be installed in +$PCP_BIN_DIR/pmtrace. A pmtrace(1) man page is available. + +The source is shipped as part of pcp as well and is installed in +$PCP_DEMOS_DIR/trace. If you have the C compiler installed, the +source and Makefile in this directory may be used to create a +functionally equivalent binary, simply by entering the command + + % make pmtrace + +The source in pmtrace.c demonstrates many of the trace services. + + +The C interface ( pmtrace.c, app1.c, app2.c, and app3.c ) +=============== + The default Makefile rules build the C applications only, so +these applications can be built simply by using the command + + % make + + +The Fortran Interface ( fapp1.f ) +===================== + To build the sample Fortran program, using either the f77 or +f90 compilers, use one of these commands + + % make fortran77 + % make fortran90 + + +The Java Interface ( japp1.java ) +================== + To build the sample Java program, and provided you have the +java compiler installed, use the command + + % make java + + Setting the environment variable $CLASSPATH to include the full +path to the trace.class file (/usr/java/classes/com/sgi/pcp) allows +the application to compile and run successfuly. +To run the demo application, after compilation type + + % java japp1 + +which passes the compiled class file into the java interpreter for +subsequent execution. + + +The pcp_trace "stub" library +============================ + To ensure that applications linked with the pcp_trace library are +not locked into being SGI-specific, a "stub" library which has all of +the pcp_trace entry points defined and simple debug switching enabled, +is provided (stub.c). This shared library can be built using + + % make -f Makefile.stub + +and is intended to be simple to port to other platforms. + + +Related manual pages +==================== + pmdatrace(1), pmtrace(1), and pmdatrace(3). + diff --git a/src/pmdas/trace/Remove b/src/pmdas/trace/Remove new file mode 100644 index 0000000..a0fa0a3 --- /dev/null +++ b/src/pmdas/trace/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the Trace PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=trace + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/trace/app1.c b/src/pmdas/trace/app1.c new file mode 100644 index 0000000..d08daf2 --- /dev/null +++ b/src/pmdas/trace/app1.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * app1.c + * + * Simple program to demonstrate use of the PCP trace performance metrics + * domain agent (PMDA(3)). This agent needs to be installed before metrics + * can be made available via the performance metrics namespace (PMNS(4)), + * and the Performance Metrics Collector Daemon (PMCD(1)). + * + * Once this program is running, the trace PMDA metrics & instances can be + * viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and + * pmview(1). To view the help text associated with each of these metrics, + * use: + * $ pminfo -tT trace + */ + +#include +#include +#include +#include + + +int +main(int argc, char **argv) +{ + int sts; + char *prog; + + prog = argv[0]; + sts = pmtracestate(PMTRACE_STATE_API|PMTRACE_STATE_COMMS|PMTRACE_STATE_PDU); + fprintf(stderr, "%s: start: %s (state=0x%x)\n", prog, + pmtraceerrstr(0), sts); /* force call to all library symbols */ + + if ((sts = pmtracebegin("simple")) < 0) { + fprintf(stderr, "%s: pmtracebegin error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + if (sleep(2) != 0) { + fprintf(stderr, "%s: sleep prematurely awaken\n", prog); + pmtraceabort("simple"); + } + if ((sts = pmtraceend("simple")) < 0) { + fprintf(stderr, "%s: pmtraceend error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtracebegin("ascanbe")) < 0) { + fprintf(stderr, "%s: pmtracebegin error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + sleep(1); + if ((sts = pmtraceend("ascanbe")) < 0) { + fprintf(stderr, "%s: pmtraceend error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtraceobs("observe", 101.0)) < 0) { + fprintf(stderr, "%s: pmtraceobs error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtracecounter("counter", 101.1)) < 0) { + fprintf(stderr, "%s: pmtracecounter error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtracepoint("imouttahere")) < 0) { + fprintf(stderr, "%s: pmtracepoint error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + exit(0); +} diff --git a/src/pmdas/trace/app2.c b/src/pmdas/trace/app2.c new file mode 100644 index 0000000..540c4ce --- /dev/null +++ b/src/pmdas/trace/app2.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * app2.c + * + * Sample program to demonstrate use of the PCP trace performance metrics + * domain agent (PMDA(3)). This agent needs to be installed before metrics + * can be made available via the performance metrics namespace (PMNS(4)), + * and the Performance Metrics Collector Daemon (PMCD(1)). + * + * Once this program is running, the trace PMDA metrics & instances can be + * viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and + * pmview(1). To view the help text associated with each of these metrics, + * use: + * $ pminfo -tT trace + */ + +#include +#include +#include +#include +#include +#include + + +#define IO_UPPER_LIMIT 1000 /* I/O ops */ +#define CPU_UPPER_LIMIT 0xffff /* iterations */ +#define TIME_UPPER_LIMIT 10 /* seconds */ + +static void io_sucker(void); +static void cpu_sucker(void); +static void time_sucker(void); +static char *prog; + +int +main(int argc, char **argv) +{ + int i, sts; + + prog = argv[0]; + srand48(time(0)); + /* uncomment this for debugging information */ + /* pmtracestate(PMTRACE_STATE_API|PMTRACE_STATE_COMMS|PMTRACE_STATE_PDU); */ + /* uncomment this to use the asynchronous protocol */ + /* pmtracestate(PMTRACE_STATE_ASYNC); */ + + for (i = 0;; i++) { + if ((sts = pmtracepoint("mainloop")) < 0) { + fprintf(stderr, "%s: mainloop point trace failed (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + exit(1); + } + switch(i % 3) { + case 0: + time_sucker(); + break; + case 1: + io_sucker(); + break; + case 2: + cpu_sucker(); + break; + } + } +} + + +static void +cpu_sucker(void) +{ + int i, j, sts; + double array[100]; + long iterations; + + if ((sts = pmtracebegin("cpu_sucker")) < 0) { + fprintf(stderr, "%s: cpu_sucker begin (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } + + iterations = lrand48() % CPU_UPPER_LIMIT; + memset((void *)array, 0, 100*sizeof(double)); + + for (i = 0; i < iterations; i++) + for (j = 0; j < 100; j++) + array[j] = (double)(j*iterations); + + if ((sts = pmtraceend("cpu_sucker")) < 0) { + fprintf(stderr, "%s: cpu_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } +} + +static void +time_sucker(void) +{ + long seconds; + int sts; + + if ((sts = pmtracebegin("time_sucker")) < 0) { + fprintf(stderr, "%s: time_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } + + seconds = lrand48() % TIME_UPPER_LIMIT; + sleep((unsigned int)seconds); + + if ((sts = pmtraceend("time_sucker")) < 0) { + fprintf(stderr, "%s: time_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } +} + +static void +io_sucker(void) +{ + long characters; + FILE *foo; + int i, sts; + + if ((sts = pmtracebegin("io_sucker")) < 0) { + fprintf(stderr, "%s: io_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } + + if ((foo = fopen("/dev/null", "rw")) == NULL) { + fprintf(stderr, "%s: io_sucker can't open /dev/null.\n", prog); + return; + } + + characters = lrand48() % IO_UPPER_LIMIT; + for (i = 0; i < characters; i++) { + fgetc(foo); + fputc('!', foo); + } + fclose(foo); + + if ((sts = pmtraceend("io_sucker")) < 0) { + fprintf(stderr, "%s: io_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } +} diff --git a/src/pmdas/trace/app3.c b/src/pmdas/trace/app3.c new file mode 100644 index 0000000..dc0e9c5 --- /dev/null +++ b/src/pmdas/trace/app3.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * app3.c + * + * Parallel program to demonstrate use of the PCP trace performance metrics + * domain agent (PMDA(3)). This agent needs to be installed before metrics + * can be made available via the performance metrics namespace (PMNS(4)), + * and the Performance Metrics Collector Daemon (PMCD(1)). + * + * Once this program is running, the trace PMDA metrics & instances can be + * viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and + * pmview(1). To view the help text associated with each of these metrics, + * use: + * $ pminfo -tT trace + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define IO_UPPER_LIMIT 1000 /* I/O ops */ +#define CPU_UPPER_LIMIT 0xffff /* iterations */ +#define TIME_UPPER_LIMIT 10 /* seconds */ + +static void * pio_sucker(void *); +static void * pcpu_sucker(void *); +static void * ptime_sucker(void *); +static char *prog; + +int +main(int argc, char **argv) +{ + int i; + pthread_t p[3]; + + prog = argv[0]; + + pthread_create(p, NULL, pio_sucker, NULL); + pthread_create(p+1, NULL, pcpu_sucker, NULL); + pthread_create(p+2, NULL, ptime_sucker, NULL); + + for (i=0; i < 3; i++) { + wait(NULL); + fprintf(stderr, "%s: reaped sproc #%d\n", prog, i); + } + + exit(0); +} + + +static void * +pcpu_sucker(void *dummy) +{ + int i, j, loops, sts; + double array[100]; + long iterations; + + for (loops = 0; loops < 10; loops++) { + if ((sts = pmtracebegin("pcpu_sucker")) < 0) { + fprintf(stderr, "%s: pcpu_sucker begin (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + + iterations = lrand48() % CPU_UPPER_LIMIT; + memset((void *)array, 0, 100*sizeof(double)); + + for (i = 0; i < iterations; i++) + for (j = 0; j < 100; j++) + array[j] = (double)(j*iterations); + + if ((sts = pmtraceend("pcpu_sucker")) < 0) { + fprintf(stderr, "%s: pcpu_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + } + fprintf(stderr, "%s: finished %d cpu-bound iterations.\n", prog, loops); + return NULL; +} + +static void * +ptime_sucker(void *dummy) +{ + long seconds; + int loops, sts; + + for (loops = 0; loops < 10; loops++) { + if ((sts = pmtracebegin("ptime_sucker")) < 0) { + fprintf(stderr, "%s: ptime_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + + seconds = lrand48() % TIME_UPPER_LIMIT; + sleep((unsigned int)seconds); + + if ((sts = pmtraceend("ptime_sucker")) < 0) { + fprintf(stderr, "%s: ptime_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + } + fprintf(stderr, "%s: finished %d timer iterations.\n", prog, loops); + return NULL; +} + +static void * +pio_sucker(void *dummy) +{ + long characters; + FILE *foo; + int i, loops, sts; + + for (loops = 0; loops < 10; loops++) { + if ((sts = pmtracebegin("pio_sucker")) < 0) { + fprintf(stderr, "%s: pio_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + + if ((foo = fopen("/dev/null", "rw")) == NULL) { + fprintf(stderr, "%s: pio_sucker can't open /dev/null.\n", prog); + return NULL; + } + + characters = lrand48() % IO_UPPER_LIMIT; + for (i = 0; i < characters; i++) { + fgetc(foo); + fputc('!', foo); + } + fclose(foo); + + if ((sts = pmtraceend("pio_sucker")) < 0) { + fprintf(stderr, "%s: pio_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + } + fprintf(stderr, "%s: finished %d io-bound iterations.\n", prog, loops); + return NULL; +} diff --git a/src/pmdas/trace/fapp1.f b/src/pmdas/trace/fapp1.f new file mode 100644 index 0000000..9fc68e8 --- /dev/null +++ b/src/pmdas/trace/fapp1.f @@ -0,0 +1,102 @@ + program fapp1 + +C Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +C +C This program is free software; you can redistribute it and/or modify it +C under the terms of the GNU General Public License as published by the +C Free Software Foundation; either version 2 of the License, or (at your +C option) any later version. +C +C This program is distributed in the hope that it will be useful, but +C WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +C or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +C for more details. +C +C You should have received a copy of the GNU General Public License along +C with this program; if not, write to the Free Software Foundation, Inc., +C 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +C fapp1.f +C +C Simple program to demonstrate use of the PCP trace performance metrics +C domain agent (PMDA(3)). This agent needs to be installed before metrics +C can be made available via the performance metrics namespace (PMNS(4)), +C and the Performance Metrics Collector Daemon (PMCD(1)). +C +C Once this program is running, the trace PMDA metrics & instances can be +C viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and +C pmview(1). To view the help text associated with each of these metrics, +C use: +C $ pminfo -tT trace +C +C The pmtracestate constants are defined in /usr/include/pcp/trace.h +C + external pmtracebegin, pmtraceend, pmtracepoint, pmtraceerrstr, pmtracestate + integer pmtracebegin, pmtraceend, pmtracepoint + integer sts + integer debug + character*5 prog + character*40 emesg + real*8 value + integer dbg_noagent, dbg_api, dbg_comms, dbg_pdu + parameter (dbg_noagent = 1, dbg_api = 2, dbg_comms = 4, dbg_pdu = 8) + + prog='fapp1' + +C Addition below is the equivalent to the C 'logical or' operator as +C trace API constants are all disjoint and the high bit is never set. + debug = (dbg_api + dbg_comms + dbg_pdu) + call pmtracestate(debug) + + sts = pmtracebegin('simple') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracebegin error: ',emesg + stop 1 + endif + call sleep(2) + sts = pmtraceend('simple') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtraceend error: ',emesg + stop 1 + endif + + sts = pmtracebegin('ascanbe') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracebegin error: ',emesg + stop 1 + endif + call sleep(1) + sts = pmtraceend('ascanbe') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtraceend error: ',emesg + stop 1 + endif + + sts = pmtracepoint('imouttahere') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracepoint error: ',emesg + stop 1 + endif + + value = 340.5 + sts = pmtraceobs('end point', value) + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtraceobs error: ',emesg + stop 1 + endif + + value = 340.6 + sts = pmtracecounter('new end point', value) + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracecounter error: ',emesg + stop 1 + endif + + end diff --git a/src/pmdas/trace/help b/src/pmdas/trace/help new file mode 100644 index 0000000..d753046 --- /dev/null +++ b/src/pmdas/trace/help @@ -0,0 +1,156 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# trace PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ TRACE.0 Instance domain "trace tag name" for trace PMDA +There is one instance for each transaction which has been seen by +the trace PMDA during the reporting period. + +@ trace.transact.count count by transaction +Count by transaction tag of transactions serviced since the trace +PMDA was started. + +@ trace.transact.rate transaction completion rate over time period +The rate at which each transaction is completed, calculated over the +trace PMDAs aggregation period. + +The exported value is equal to the count of transactions completed +during the aggregation interval, divided by the aggregation interval. + +@ trace.transact.ave_time average transaction time over time period +The average time taken to complete each transaction, calculated over +the trace PMDAs aggregation period. + +The exported value is equal to the sum of the transaction completion +times seen during the aggregation interval divided by the number of +transactions completed during the aggregation interval. + +@ trace.transact.max_time maximum time per transaction +Maximum recorded service time per transaction tag within the current +reporting period. + +@ trace.transact.min_time minimum time per transaction +Minimum recorded service time per transaction tag within the current +reporting period. + +@ trace.transact.total_time total time per transaction +Cumulative time spent processing each transaction since the trace PMDA +was started. + +@ trace.point.count count of point function executions +Count by point tag of marked application points serviced since the +trace PMDA was started. + +@ trace.point.rate point rate over time period +The rate at which execution points are completed, calculated over the +aggregation period in use by the trace PMDA. + +The exported value is equal to the count of successful pmtracepoint(3) +calls made during the aggregation interval, divided by the aggregation +interval. + +@ trace.counter.count count of counter values received +Count, by counter tag, of `counter' values received since the trace +PMDA was started. + +@ trace.counter.rate counter value received rate over time period +The rate at which execution counters are received, calculated over +the aggregation period in use by the trace PMDA. + +The exported value is equal to the count of pmtracecounter(3) calls made +during the aggregation interval, divided by the aggregation interval. + +@ trace.counter.value counter value at last observation +The numeric counter value associated with the last seen counter tag, +since the trace PMDA was started. + +@ trace.observe.count count of observations +Count, by observation tag, of application `observe' points serviced +since the trace PMDA was started. + +@ trace.observe.rate observation rate over time period +The rate at which execution observations are completed, calculated +over the aggregation period in use by the trace PMDA. + +The exported value is equal to the count of pmtraceobs(3) calls made +during the aggregation interval, divided by the aggregation interval. + +@ trace.observe.value value at last observation +The numeric value associated with the last seen observation, since +the trace PMDA was started. + +@ trace.control.period reporting time period +Time (in seconds) over which trace performance data will be gathered. +Any transaction or point trace data seen by the trace PMDA during the +period will be included in the set of exported values. + +@ trace.control.interval update interval within time period +The update interval (within the overall period) at which the current +working set of performance data maintained within the trace PMDA will +be switched into the set of historical data buffers, which are then +used to calculate the exported performance metric values. +This value is directly calculated from the overall time period and the +number of buckets of historical data being maintained within the trace +PMDA. + +For example, if the overall period is 60 seconds and the number of +historical data buckets being maintained is 12, then the buffers will +be rotated once every 5 seconds. + +@ trace.control.buckets number of historical buffers +The number of buffers of historical data maintained by the trace PMDA. +Each bucket contains all transaction and event performance data seen +over a set time interval within the overall reporting time period. + +@ trace.control.port port number for client connections +The TCP/IP port number which the trace PMDA is waiting for client +connections on. + +This has been set as either the default (4322), via the command line, +or via the PMDA_TRACE_PORT environment variable when the trace PMDA +was started. + +@ trace.control.reset clear all tags known to the trace PMDA +Storing any value into this metric with pmstore(1) will cause the trace +PMDA to clear all tags for all metrics from its historical buffers and +begin afresh. + +This is most useful when the instance domains of the trace metrics have +become cluttered with unwanted instances, and the instance domains need +to be refreshed without restarting pmcd(1). + +@ trace.control.debug set the debug level in the trace PMDA +Storing values into this metric with pmstore(1) allows the level of +diagnostic output from the trace PMDA to be controlled. + +By default, the diagnostic output will be written to the file +$PCP_LOG_DIR/pmcd/trace.log. diff --git a/src/pmdas/trace/japp1.java b/src/pmdas/trace/japp1.java new file mode 100644 index 0000000..8c557d4 --- /dev/null +++ b/src/pmdas/trace/japp1.java @@ -0,0 +1,46 @@ +// +// Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +public class japp1 { + public static void main(String[] args) { + int sts; + trace pcp = new trace(); + + pcp.pmtracestate(pcp.PMTRACE_STATE_API); + + sts = pcp.pmtracebegin("transacting in java"); + if (sts < 0) + System.out.println("pmtracebegin error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtraceend("transacting in java"); + if (sts < 0) + System.out.println("pmtraceend error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtracepoint("java point"); + if (sts < 0) + System.out.println("pmtracepoint error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtraceobs("observing from java", 789.034018); + if (sts < 0) + System.out.println("pmtraceobs error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtracecounter("counter from java", 789.034019); + if (sts < 0) + System.out.println("pmtracecounter error: " + pcp.pmtraceerrstr(sts)); + } +} diff --git a/src/pmdas/trace/pmns b/src/pmdas/trace/pmns new file mode 100644 index 0000000..c7ef847 --- /dev/null +++ b/src/pmdas/trace/pmns @@ -0,0 +1,62 @@ +/* + * Metrics for trace PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +trace { + transact + point + observe + counter + control +} + +trace.transact { + count TRACE:0:0 + rate TRACE:0:1 + ave_time TRACE:0:2 + min_time TRACE:0:3 + max_time TRACE:0:4 + total_time TRACE:0:5 +} + +trace.point { + count TRACE:0:6 + rate TRACE:0:7 +} + +trace.observe { + count TRACE:0:8 + rate TRACE:0:9 + value TRACE:0:10 +} + +trace.control { + period TRACE:0:11 + interval TRACE:0:12 + buckets TRACE:0:13 + port TRACE:0:14 + reset TRACE:0:15 + debug TRACE:0:16 +} + +trace.counter { + count TRACE:0:17 + rate TRACE:0:18 + value TRACE:0:19 +} diff --git a/src/pmdas/trace/root b/src/pmdas/trace/root new file mode 100644 index 0000000..1ba8c5a --- /dev/null +++ b/src/pmdas/trace/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { trace } + +#include "pmns" + diff --git a/src/pmdas/trace/src/GNUmakefile b/src/pmdas/trace/src/GNUmakefile new file mode 100644 index 0000000..efb5e43 --- /dev/null +++ b/src/pmdas/trace/src/GNUmakefile @@ -0,0 +1,57 @@ +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = trace +DOMAIN = TRACE +CMDTARGET = pmdatrace$(EXECSUFFIX) +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +CFILES = trace.c client.c comms.c data.c pmda.c +HFILES = data.h client.h comms.h + +LCFLAGS = -I$(TOPDIR)/src/libpcp_trace/src +LLDFLAGS = -L$(TOPDIR)/src/libpcp_trace/src +LLDLIBS = -lpcp_trace $(PCP_PMDALIB) + +LDIRT = *.log *.dir *.pag domain.h $(TARGETS) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(CMDTARGET) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/$(CMDTARGET) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h +else +build-me: +install: +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +comms.o trace.o pmda.o: domain.h + +domain.h: ../../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/trace/src/client.c b/src/pmdas/trace/src/client.c new file mode 100644 index 0000000..c3224dd --- /dev/null +++ b/src/pmdas/trace/src/client.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2012-2013 Red Hat. + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "trace_dev.h" +#include "client.h" +#include "comms.h" + +extern fd_set fds; + +int nclients; /* number of entries in array */ +int maxfd; /* largest fd currently in use */ +client_t *clients; /* array of clients */ + +#define MIN_CLIENTS_ALLOC 8 +static int clientsize; + +static int newClient(void); + +client_t * +acceptClient(int reqfd) +{ + int i, fd; + __pmSockLen addrlen; + + i = newClient(); + addrlen = __pmSockAddrSize(); + fd = __pmAccept(reqfd, clients[i].addr, &addrlen); + if (fd == -1) { + __pmNotifyErr(LOG_ERR, "acceptClient(%d) accept: %s", + reqfd, netstrerror()); + return NULL; + } + if (fd > maxfd) + maxfd = fd; + FD_SET(fd, &fds); + clients[i].fd = fd; + clients[i].status.connected = 1; + clients[i].status.padding = 0; + clients[i].status.protocol = 1; /* sync */ + return &clients[i]; +} + +static int +newClient(void) +{ + int i, j; + + for (i = 0; i < nclients; i++) + if (!clients[i].status.connected) + break; + + if (i == clientsize) { + clientsize = clientsize ? clientsize * 2 : MIN_CLIENTS_ALLOC; + clients = (client_t *) realloc(clients, sizeof(client_t)*clientsize); + if (clients == NULL) + __pmNoMem("newClient", sizeof(client_t)*clientsize, PM_FATAL_ERR); + for (j = i; j < clientsize; j++) + clients[j].addr = NULL; + } + clients[i].addr = __pmSockAddrAlloc(); + if (clients[i].addr == NULL) + __pmNoMem("newClient", __pmSockAddrSize(), PM_FATAL_ERR); + if (i >= nclients) + nclients = i + 1; + return i; +} + +void +deleteClient(client_t *cp) +{ + int i; + + for (i = 0; i < nclients; i++) + if (cp == &clients[i]) + break; + + if (i == nclients) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmNotifyErr(LOG_ERR, "deleteClient: tried to delete non-existent client"); + } +#endif + return; + } + if (cp->fd != -1) { + __pmtracenomoreinput(cp->fd); + FD_CLR(cp->fd, &fds); + close(cp->fd); + } + if (cp->fd == maxfd) { + maxfd = -1; + for (i = 0; i < nclients; i++) + if (clients[i].fd > maxfd) + maxfd = clients[i].fd; + } + __pmSockAddrFree(cp->addr); + cp->addr = NULL; + cp->status.connected = 0; + cp->status.padding = 0; + cp->status.protocol = 1; /* sync */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "deleteClient: client removed (fd=%d)", cp->fd); +#endif + cp->fd = -1; +} + +void +showClients(void) +{ + int i; + + fprintf(stderr, "%s: %d connected clients:\n", pmProgname, nclients); + fprintf(stderr, " fd type conn client connection from\n" + " == ===== ==== ======================\n"); + for (i=0; i < nclients; i++) { + char *hostName; + char *hostAddr; + + fprintf(stderr, " %3d", clients[i].fd); + fprintf(stderr, " %s ", clients[i].status.protocol == 1 ? "sync ":"async"); + fprintf(stderr, "%s ", clients[i].status.connected == 1 ? "up ":"down"); + hostName = __pmGetNameInfo(clients[i].addr); + if (hostName == NULL) { + hostAddr = __pmSockAddrToString(clients[i].addr); + fprintf(stderr, "%s", hostAddr); + free(hostAddr); + } else { + fprintf(stderr, "%-40.40s", hostName); + free(hostName); + } + if (clients[i].denyOps != 0) { + fprintf(stderr, " "); + if (clients[i].denyOps & TR_OP_SEND) + fprintf(stderr, "send "); + } + + fputc('\n', stderr); + } + fputc('\n', stderr); +} diff --git a/src/pmdas/trace/src/client.h b/src/pmdas/trace/src/client.h new file mode 100644 index 0000000..9cb3ad2 --- /dev/null +++ b/src/pmdas/trace/src/client.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CLIENT_H +#define CLIENT_H + +typedef struct { + int fd; /* socket descriptor */ + __pmSockAddr *addr; /* address of client */ + struct { /* connection status */ + unsigned int connected : 1; /* client connected */ + unsigned int version : 8; /* client pdu version */ + unsigned int protocol : 1; /* synchronous or not */ + unsigned int padding :22; /* currently unused */ + } status; + unsigned int denyOps; +} client_t; + +extern client_t *acceptClient(int); +extern void deleteClient(client_t *); +extern void showClients(void); + +#endif /* CLIENT_H */ diff --git a/src/pmdas/trace/src/comms.c b/src/pmdas/trace/src/comms.c new file mode 100644 index 0000000..4432c05 --- /dev/null +++ b/src/pmdas/trace/src/comms.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012-2013 Red Hat. All Rights Reserved. + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "trace.h" +#include "trace_dev.h" +#include "domain.h" +#include "client.h" +#include "comms.h" + +extern struct timeval interval; +extern int readData(int, int *); +extern void timerUpdate(void); + +extern int maxfd; +extern int nclients; +extern client_t *clients; +extern int ctlport; /* control port number */ +static int ctlfd; /* fd for control port */ +static int pmcdfd; /* fd for pmcd */ + +void alarming(int, void *); +static void hangup(int); +static int getcport(void); + +/* currently in-use fd mask */ +fd_set fds; + +/* the AF event number */ +int afid = -1; + +void +traceMain(pmdaInterface *dispatch) +{ + client_t *cp; + fd_set readyfds; + int nready, i, pdutype, sts, protocol; + + ctlfd = getcport(); + pmcdfd = __pmdaInFd(dispatch); + maxfd = (ctlfd > pmcdfd) ? (ctlfd):(pmcdfd); + FD_ZERO(&fds); + FD_SET(ctlfd, &fds); + FD_SET(pmcdfd, &fds); + + signal(SIGHUP, hangup); + + /* arm interval timer */ + if ((afid = __pmAFregister(&interval, NULL, alarming)) < 0) { + __pmNotifyErr(LOG_ERR, "error registering asynchronous event handler"); + exit(1); + } + + for (;;) { + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, NULL); + + if (nready == 0) + continue; + else if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } + continue; + } + + __pmAFblock(); + if (FD_ISSET(pmcdfd, &readyfds)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd request [fd=%d]", pmcdfd); +#endif + if (__pmdaMainPDU(dispatch) < 0) { + __pmAFunblock(); + exit(1); /* fatal if we lose pmcd */ + } + } + /* handle request on control port */ + if (FD_ISSET(ctlfd, &readyfds)) { + if ((cp = acceptClient(ctlfd)) != NULL) { + sts = __pmAccAddClient(cp->addr, &cp->denyOps); + if (sts == PM_ERR_PERMISSION) + sts = PMTRACE_ERR_PERMISSION; + else if (sts == PM_ERR_CONNLIMIT) + sts = PMTRACE_ERR_CONNLIMIT; + else if (sts >= 0) + sts = TRACE_PDU_VERSION; + __pmtracesendack(cp->fd, sts); + if (sts < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(cp->addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: connect refused, denyOps=0x%x: %s", + hostAddr, cp->fd, cp->denyOps, pmtraceerrstr(sts)); + free(hostAddr); + } +#endif + deleteClient(cp); + } + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(cp->addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: new connection, denyOps=0x%x", + hostAddr, cp->fd, cp->denyOps); + free(hostAddr); + } +#endif + ; + } + } + } + for (i = 0; i < nclients; i++) { + if (!clients[i].status.connected) + continue; + if (clients[i].denyOps & TR_OP_SEND) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: send denied, denyOps=0x%x", + hostAddr, clients[i].fd, clients[i].denyOps); + free(hostAddr); + } +#endif + __pmtracesendack(clients[i].fd, PMTRACE_ERR_PERMISSION); + __pmAccDelClient(clients[i].addr); + deleteClient(&clients[i]); + } + else if (FD_ISSET(clients[i].fd, &readyfds)) { + protocol = 1; /* default to synchronous */ + do { + if ((pdutype = readData(clients[i].fd, &protocol)) < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: close connection", + hostAddr, clients[i].fd); + free(hostAddr); + } +#endif + __pmAccDelClient(clients[i].addr); + deleteClient(&clients[i]); + } + else { + clients[i].status.protocol = protocol; + if (clients[i].status.connected) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: %s ACK (type=%d)", + hostAddr, clients[i].fd, + protocol ? "sending" : "no", pdutype); + free(hostAddr); + } +#endif + if (protocol == 1) { + sts = __pmtracesendack(clients[i].fd, pdutype); + if (sts < 0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_ERR, "client %s [fd=%d]: ACK send failed (type=%d): %s", + hostAddr, clients[i].fd, + pdutype, pmtraceerrstr(sts)); + free(hostAddr); + } + } + } + } + } while (__pmtracemoreinput(clients[i].fd)); + } + } + __pmAFunblock(); + } +} + + +void +alarming(int sig, void *ptr) +{ + timerUpdate(); +} + +static void +hangup(int sig) +{ + showClients(); + signal(SIGHUP, hangup); +} + +/* + * Create socket for incoming connections and bind to it an address for + * clients to use. Only returns if it succeeds (exits on failure). + */ +static int +getcport(void) +{ + int fd; + int i=1, one=1, sts; + struct sockaddr_in myAddr; + struct linger noLinger = {1, 0}; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + __pmNotifyErr(LOG_ERR, "getcport: socket: %s", netstrerror()); + exit(1); + } + /* avoid 200 ms delay */ + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &i, + (__pmSockLen)sizeof(i)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(nodelay): %s", + netstrerror()); + exit(1); + } + /* don't linger on close */ + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &noLinger, + (__pmSockLen)sizeof(noLinger)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(nolinger): %s", + netstrerror()); + exit(1); + } +#ifndef IS_MINGW + /* ignore dead client connections */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, + (__pmSockLen)sizeof(one)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(reuseaddr): %s", + netstrerror()); + exit(1); + } +#else + /* see MSDN tech note: "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" */ + if (setsockopt(sfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, + (__pmSockLen)sizeof(one)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(excladdruse): %s", + netstrerror()); + exit(1); + } +#endif + + if (ctlport == -1) { + /* + * check for port info in the environment + */ + char *env_str; + if ((env_str = getenv(TRACE_ENV_PORT)) != NULL) { + char *end_ptr; + + ctlport = (int)strtol(env_str, &end_ptr, 0); + if (*end_ptr != '\0' || ctlport < 0) { + __pmNotifyErr(LOG_WARNING, "env port is bogus (%s)", env_str); + ctlport = TRACE_PORT; + } + } + else + ctlport = TRACE_PORT; + } + + /* TODO: IPv6 */ + memset(&myAddr, 0, sizeof(myAddr)); + myAddr.sin_family = AF_INET; + myAddr.sin_addr.s_addr = htonl(INADDR_ANY); + myAddr.sin_port = htons(ctlport); + + sts = bind(fd, (const struct sockaddr *)&myAddr, sizeof(myAddr)); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "bind(%d): %s", ctlport, netstrerror()); + exit(1); + } + sts = listen(fd, 5); /* Max. of 5 pending connection requests */ + if (sts == -1) { + __pmNotifyErr(LOG_ERR, "listen: %s", netstrerror()); + exit(1); + } + + return fd; +} diff --git a/src/pmdas/trace/src/comms.h b/src/pmdas/trace/src/comms.h new file mode 100644 index 0000000..b564f4d --- /dev/null +++ b/src/pmdas/trace/src/comms.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef COMMS_H +#define COMMS_H + +#define TR_OP_NONE 0x0 +#define TR_OP_SEND 0x1 +#define TR_OP_ALL 0x1 + +#endif /* COMMS_H */ diff --git a/src/pmdas/trace/src/data.c b/src/pmdas/trace/src/data.c new file mode 100644 index 0000000..2db8fb4 --- /dev/null +++ b/src/pmdas/trace/src/data.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pmapi.h" +#include "impl.h" +#include "data.h" + +int +instcmp(void *a, void *b) +{ + instdata_t *aa = (instdata_t *)a; + instdata_t *bb = (instdata_t *)b; + + if (aa == NULL || bb == NULL) + return 0; + if (aa->type != bb->type) + return 0; + return !strcmp(aa->tag, bb->tag); +} + +void +instdel(void *a) +{ + instdata_t *k = (instdata_t *)a; + + if (k != NULL) { + if (k->tag != NULL) + free(k->tag); + free(k); + } +} + +void +instprint(__pmHashTable *t, void *e) +{ + instdata_t *i = (instdata_t *)e; + + __pmNotifyErr(LOG_DEBUG, "Instance history table entry\n" + "Name: '%s'\n type: %d\n inst: %d\n", + i->tag, i->type, i->instid); +} + + +int +datacmp(void *a, void *b) +{ + hashdata_t *aa = (hashdata_t *)a; + hashdata_t *bb = (hashdata_t *)b; + + if (aa == NULL || bb == NULL) + return 0; + if (aa->tracetype != bb->tracetype) + return 0; + return !strcmp(aa->tag, bb->tag); +} + +void +datadel(void *a) +{ + hashdata_t *k = (hashdata_t *)a; + + if (k != NULL) { + if (k->tag != NULL) + free(k->tag); + free(k); + } +} + +void +dataprint(__pmHashTable *t, void *e) +{ + hashdata_t *h = (hashdata_t *)e; + + __pmNotifyErr(LOG_DEBUG, "PMDA hash table entry\n" + "Name: '%s'\n filedes: %d\n" + " type: %d\n length: %d\n" + " padding: %d\n count: %d\n" + " txmin: %f\n txmax: %f\n" + " txsum: %f\nSize: %d\n" + "-----------\n", + h->tag, h->fd, h->tracetype, h->taglength, h->padding, + (int)h->txcount, h->txmin, h->txmax, h->txsum, (int)sizeof(*h)); +} + + +#ifdef PCP_DEBUG +void +debuglibrary(int flag) +{ + extern int __pmstate; + int state; + + state = pmtracestate(0); + if (flag & DBG_TRACE_APPL0) + state |= PMTRACE_STATE_COMMS; + if (flag & DBG_TRACE_PDU) + state |= PMTRACE_STATE_PDU; + if (flag & DBG_TRACE_PDUBUF) + state |= PMTRACE_STATE_PDUBUF; + if (flag == 0) + __pmstate = 0; + else + pmtracestate(state); +} +#endif + + diff --git a/src/pmdas/trace/src/data.h b/src/pmdas/trace/src/data.h new file mode 100644 index 0000000..e4c0352 --- /dev/null +++ b/src/pmdas/trace/src/data.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef TRACE_DATA_H +#define TRACE_DATA_H + +#include "hash.h" +#include "trace.h" +#include "trace_dev.h" + +typedef struct { + char *tag; + unsigned int type; + unsigned int instid; +} instdata_t; + +typedef struct __pmHashTab hashtable_t; + +int instcmp(void *a, void *b); +void instdel(void *a); +void instprint(hashtable_t *t, void *e); + +typedef struct { + char *tag; + unsigned int id; + int fd; + unsigned int tracetype : 8; + unsigned int taglength : 8; + unsigned int padding : 16; + __uint64_t realcount; /* real total seen by the PMDA */ + double realtime; /* total time for transactions */ + __int32_t txcount; /* count this interval or -1 */ + double txmin; /* minimum value this interval */ + double txmax; /* maximum value this interval */ + double txsum; /* summed across the interval */ +} hashdata_t; + +int datacmp(void *a, void *b); +void datadel(void *a); +void dataprint(hashtable_t *t, void *e); + +typedef struct { + unsigned int numstats : 8; /* number of entries in this table */ + unsigned int working : 1; /* this the current working table? */ + hashtable_t *stats; +} statlist_t; + +typedef struct { + statlist_t *ring; /* points to all statistics */ + unsigned int level; /* controls reporting level */ +} ringbuf_t; + +#ifdef PCP_DEBUG +void debuglibrary(int); +#endif + +#endif /* TRACE_DATA_H */ diff --git a/src/pmdas/trace/src/pmda.c b/src/pmdas/trace/src/pmda.c new file mode 100644 index 0000000..36560aa --- /dev/null +++ b/src/pmdas/trace/src/pmda.c @@ -0,0 +1,228 @@ +/* + * Trace PMDA - process level transaction monitoring for libpcp_trace processes + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 1997-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "trace.h" +#include "trace_dev.h" +#include "comms.h" + +#define DEFAULT_TIMESPAN 60 /* one minute */ +#define DEFAULT_BUFSIZE 5 /* twelve second update */ + +struct timeval timespan = { DEFAULT_TIMESPAN, 0 }; +struct timeval interval; +unsigned int rbufsize = DEFAULT_BUFSIZE; +int ctlport = -1; +char *ctlsock; + +static char mypath[MAXPATHLEN]; +static char *username; + +extern void traceInit(pmdaInterface *dispatch); +extern void traceMain(pmdaInterface *dispatch); +extern int updateObserveValue(const char *); +extern int updateCounterValue(const char *); +extern void debuglibrary(int); + +static void +usage(void) +{ + fprintf(stderr, +"Usage: %s [options]\n\ +\n\ +Options:\n\ + -d domain use domain (numeric) for metrics domain of PMDA\n\ + -l logfile write log into logfile rather than using default file\n\ + -A access host based access control\n\ + -I port expect programs to connect on given inet port (number/name)\n\ + -M username user account to run under (default \"pcp\")\n\ + -N buckets number of historical data buffers maintained\n\ + -T period time over which samples are considered (default 60 seconds)\n\ + -U units export observation values using the given units\n\ + -V units export counter values using the given units\n", + pmProgname); + exit(1); +} + +static char * +squash(char *str, int *offset) +{ + char *hspec = NULL; + char *p = str; + int i = 0; + + hspec = strdup(str); /* make sure we have space */ + *offset = 0; + while (isspace((int)*p)) { p++; (*offset)++; } + while (p && *p != ':' && *p != '\0') { + hspec[i++] = *p; + p++; + } + hspec[i] = '\0'; + *offset += i; + return hspec; +} + +static int +parseAuth(char *spec) +{ + static int first = 1; + int offset, maxconn, specops = TR_OP_ALL, denyops; + char *p, *endnum; + + if (first) { + if (__pmAccAddOp(TR_OP_SEND) < 0) { + __pmNotifyErr(LOG_ERR, "failed to add send auth operation"); + return -1; + } + first = 0; + } + + if (strncasecmp(spec, "disallow:", 9) == 0) { + p = squash(&spec[9], &offset); + if (p == NULL || p[0] == '\0') { + fprintf(stderr, "%s: invalid disallow (%s)\n", pmProgname, spec); + if (p) + free(p); + return -1; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "deny: host '%s'\n", p); +#endif + denyops = TR_OP_SEND; + if (__pmAccAddHost(p, specops, denyops, 0) < 0) + __pmNotifyErr(LOG_ERR, "failed to add authorisation (%s)", p); + free(p); + } + else if (strncasecmp(spec, "allow:", 6) == 0) { + p = squash(&spec[6], &offset); + if (p == NULL || p[0] == '\0') { + fprintf(stderr, "%s: invalid allow (%s)\n", pmProgname, spec); + if (p) + free(p); + return -1; + } + offset += 7; + maxconn = (int)strtol(&spec[offset], &endnum, 10); + if (*endnum != '\0' || maxconn < 0) { + fprintf(stderr, "%s: bogus max connection in '%s'\n", pmProgname, + &spec[offset]); + free(p); + return -1; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "allow: host '%s', maxconn=%d\n", p, maxconn); +#endif + denyops = TR_OP_NONE; + if (__pmAccAddHost(p, specops, denyops, maxconn) < 0) + __pmNotifyErr(LOG_ERR, "failed to add authorisation (%s)", p); + free(p); + } + else { + fprintf(stderr, "%s: access spec is invalid (%s)\n", pmProgname, spec); + return -1; + } + return 0; +} + +int +main(int argc, char **argv) +{ + pmdaInterface dispatch; + char *endnum; + int err = 0; + int sep = __pmPathSeparator(); + int c = 0; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "trace" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, TRACE, + "trace.log", mypath); + + /* need - port, as well as time interval and time span for averaging */ + while ((c = pmdaGetOpt(argc, argv, "A:D:d:I:l:T:M:N:U:V:?", + &dispatch, &err)) != EOF) { + switch(c) { + case 'A': + if (parseAuth(optarg) < 0) + err++; + /* add optarg to access control list */ + break; + case 'I': + ctlport = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0' || ctlport < 0) + ctlsock = optarg; + break; + case 'M': + username = optarg; + break; + case 'N': + rbufsize = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0' || rbufsize < 1) { + fprintf(stderr, "%s: -N requires a positive number.\n", pmProgname); + err++; + } + break; + case 'T': + if (pmParseInterval(optarg, ×pan, &endnum) < 0) { + fprintf(stderr, "%s: -T requires a time interval: %s\n", + pmProgname, endnum); + free(endnum); + err++; + } + break; + case 'U': + if (updateObserveValue(optarg) < 0) + err++; + break; + case 'V': + if (updateCounterValue(optarg) < 0) + err++; + break; + default: + err++; + } + } + + if (err) + usage(); + + interval.tv_sec = (int)(timespan.tv_sec / rbufsize); + interval.tv_usec = (long)((timespan.tv_sec % rbufsize) * 1000000); + rbufsize++; /* reserve space for the `working' buffer */ + +#ifdef PCP_DEBUG + debuglibrary(pmDebug); +#endif + + pmdaOpenLog(&dispatch); + __pmSetProcessIdentity(username); + traceInit(&dispatch); + pmdaConnect(&dispatch); + traceMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/trace/src/trace.c b/src/pmdas/trace/src/trace.c new file mode 100644 index 0000000..f57c4bb --- /dev/null +++ b/src/pmdas/trace/src/trace.c @@ -0,0 +1,1151 @@ +/* + * Copyright (c) 1997-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "data.h" +#include "trace_dev.h" + +static pmdaIndom indomtab[] = { /* list of trace metric instance domains */ +#define TRANSACT_INDOM 0 + { TRANSACT_INDOM, 0, NULL }, /* dynamically updated */ +#define POINT_INDOM 1 + { POINT_INDOM, 0, NULL }, /* dynamically updated */ +#define OBSERVE_INDOM 2 + { OBSERVE_INDOM, 0, NULL }, /* dynamically updated */ +#define COUNTER_INDOM 3 + { COUNTER_INDOM, 0, NULL }, /* dynamically updated */ +}; + +static int transacts; /* next instance# to allocate */ +static int points; /* next instance# to allocate */ +static int counters; /* next instance# to allocate */ +static int observes; /* next instance# to allocate */ +static int tsortflag; /* need sort on next request? */ +static int psortflag; /* need sort on next request? */ +static int csortflag; /* need sort on next request? */ +static int osortflag; /* need sort on next request? */ + +/* all metrics supported in this PMDA - one table entry for each */ +static pmdaMetric metrictab[] = { +/* transact.count */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U64, TRANSACT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE)} }, +/* transact.rate */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* transact.ave_time */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,-1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* transact.min_time */ + { NULL, + { PMDA_PMID(0,3), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* transact.max_time */ + { NULL, + { PMDA_PMID(0,4), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* transact.total_time */ + { NULL, + { PMDA_PMID(0,5), PM_TYPE_DOUBLE, TRANSACT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* point.count */ + { NULL, + { PMDA_PMID(0,6), PM_TYPE_U64, POINT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE)} }, +/* point.rate */ + { NULL, + { PMDA_PMID(0,7), PM_TYPE_FLOAT, POINT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* observe.count */ + { NULL, + { PMDA_PMID(0,8), PM_TYPE_U64, OBSERVE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE)} }, +/* observe.rate */ + { NULL, + { PMDA_PMID(0,9), PM_TYPE_FLOAT, OBSERVE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* observe.value */ + { NULL, + { PMDA_PMID(0,10), PM_TYPE_DOUBLE, OBSERVE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, /* this may be modified at startup */ +/* control.timespan */ + { NULL, + { PMDA_PMID(0,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* control.interval */ + { NULL, + { PMDA_PMID(0,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* control.buckets */ + { NULL, + { PMDA_PMID(0,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, +/* control.port */ + { NULL, + { PMDA_PMID(0,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, +/* control.reset */ + { NULL, + { PMDA_PMID(0,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, +/* control.debug */ + { NULL, + { PMDA_PMID(0,16), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0) }, }, +/* counter.count */ + { NULL, + { PMDA_PMID(0,17), PM_TYPE_U64, COUNTER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE) }, }, +/* counter.rate */ + { NULL, + { PMDA_PMID(0,18), PM_TYPE_FLOAT, COUNTER_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE) }, }, +/* counter.value */ + { NULL, + { PMDA_PMID(0,19), PM_TYPE_DOUBLE, COUNTER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,0, 0,0,0) }, }, /* this may be modified at startup */ +}; + +extern void __pmdaStartInst(pmInDom indom, pmdaExt *pmda); + +extern int ctlport; +extern unsigned int rbufsize; +extern struct timeval timespan; +extern struct timeval interval; + +static ringbuf_t ringbuf; /* *THE* ring buffer of trace data */ +static hashtable_t summary; /* globals + recent ringbuf summary */ +static hashtable_t history; /* holds every instance seen so far */ +static unsigned int rpos; /* `working' buffer, within ringbuf */ +static unsigned int dosummary = 1; /* summary refreshed this interval? */ +static unsigned int tindomsize = 0; /* updated local to fetch only */ +static unsigned int pindomsize = 0; /* updated local to fetch only */ +static unsigned int oindomsize = 0; /* updated local to fetch only */ +static unsigned int cindomsize = 0; /* updated local to fetch only */ + /* note: {t,p,o,c}indomsize are only valid when dosummary equals zero */ + + +/* allow configuration of trace.observe.value/trace.counter.value units */ +static int +updateValueUnits(const char *str, int offset) +{ + int units[6], i, sts = 0; + char *s, *sptr, *endp; + + if ((sptr = strdup(str)) == NULL) + return -oserror(); + s = sptr; + + for (i = 0; i < 6; i++) { + if ((s = strtok((i==0 ? sptr : NULL), ",")) == NULL) { + fprintf(stderr, "%s: token parse error in string \"%s\"\n", + pmProgname, str); + sts = -1; + goto leaving; + } + units[i] = (int)strtol(s, &endp, 10); + if (*endp) { + fprintf(stderr, "%s: integer parse error for substring \"%s\"\n", + pmProgname, s); + sts = -1; + goto leaving; + } + } + + /* update table entry for this value metric */ + metrictab[offset].m_desc.units.dimSpace = units[0]; + metrictab[offset].m_desc.units.dimTime = units[1]; + metrictab[offset].m_desc.units.dimCount = units[2]; + metrictab[offset].m_desc.units.scaleSpace = units[3]; + metrictab[offset].m_desc.units.scaleTime = units[4]; + metrictab[offset].m_desc.units.scaleCount = units[5]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: value metric units updated using \"%s\"\n" + "dimSpace=%d, dimTime=%d, dimCount=%d, scaleSpace=%d, " + "scaleTime=%d, scaleCount=%d\n", pmProgname, str, + units[0], units[1], units[2], units[3], units[4], units[5]); + } +#endif + +leaving: + if (sptr != NULL) + free(sptr); + return sts; +} + +int updateObserveValue(const char *str) { return updateValueUnits(str, 10); } +int updateCounterValue(const char *str) { return updateValueUnits(str, 19); } + + +static int +compareInstance(const void *a, const void *b) +{ + pmdaInstid *aa = (pmdaInstid *)a; + pmdaInstid *bb = (pmdaInstid *)b; + return aa->i_inst - bb->i_inst; +} + +/* + * sort reset instance domains to be friendly to pmclients + * (only PMDA knows when optimal time to sort these infrequently-changing sets) + */ +static void +indomSortCheck(void) +{ + if (tsortflag) { + qsort(indomtab[TRANSACT_INDOM].it_set, + indomtab[TRANSACT_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + tsortflag = 0; + } + if (psortflag) { + qsort(indomtab[POINT_INDOM].it_set, + indomtab[POINT_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + psortflag = 0; + } + if (osortflag) { + qsort(indomtab[OBSERVE_INDOM].it_set, + indomtab[OBSERVE_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + osortflag = 0; + } + if (csortflag) { + qsort(indomtab[COUNTER_INDOM].it_set, + indomtab[COUNTER_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + csortflag = 0; + } +} + +/* + * wrapper for pmdaInstance which we need to ensure is called with the + * _sorted_ contents of the instance domain. + */ +static int +traceInstance(pmInDom indom, int foo, char *bar, __pmInResult **iresp, pmdaExt + *pmda) +{ + indomSortCheck(); + return pmdaInstance(indom, foo, bar, iresp, pmda); +} + +/* + * `summary' table deletion may add to the `history' table. + */ +void +summarydel(void *a) +{ + hashdata_t *k = (hashdata_t *)a; + instdata_t check; + + check.tag = k->tag; + check.type = k->tracetype; + check.instid = k->id; + if (__pmhashlookup(&history, check.tag, &check) == NULL) { + if (__pmhashinsert(&history, check.tag, &check) < 0) + __pmNotifyErr(LOG_ERR, "history table insert failure - '%s' " + "instance will not maintain its instance number.", check.tag); + } + if (k != NULL) + free(k); /* don't free k->tag - its in the history table */ +} + +/* + * Processes data from pcp_trace-linked client programs. + * + * Return negative only on fd-related errors, as that connection will + * later be closed. Other errors - report in log file but continue. + */ +int +readData(int clientfd, int *protocol) +{ + __pmTracePDU *result; + double data; + hashdata_t newhash; + hashdata_t *hptr; + hashdata_t hash; + char *tag; + int type, taglen, sts; + int freeflag=0; + + if ((sts = __pmtracegetPDU(clientfd, TRACE_TIMEOUT_NEVER, &result)) < 0) { + __pmNotifyErr(LOG_ERR, "bogus PDU read - %s", pmtraceerrstr(sts)); + return -1; + } + else if (sts == TRACE_PDU_DATA) { + if ((sts = __pmtracedecodedata(result, &tag, &taglen, + &type, protocol, &data)) < 0) + return -1; + if (type < TRACE_FIRST_TYPE || type > TRACE_LAST_TYPE) { + __pmNotifyErr(LOG_ERR, "unknown trace type for '%s' (%d)", tag, type); + free(tag); + return -1; + } + newhash.tag = tag; + newhash.taglength = taglen; + newhash.tracetype = type; + } + else if (sts == 0) { /* client has exited - cleanup in mainloop */ + return -1; + } + else { /* unknown PDU type - bail & later kill connection */ + __pmNotifyErr(LOG_ERR, "unknown PDU - expected data PDU" + " (not type #%d)", sts); + return -1; + } + + /* + * First, update the global summary table with this new data + */ + if ((hptr = __pmhashlookup(&summary, tag, &newhash)) == NULL) { + instdata_t check, *iptr; + int size, index, indom; + + check.tag = newhash.tag; + check.type = newhash.tracetype; + if ((iptr = __pmhashlookup(&history, check.tag, &check)) != NULL) { + newhash.id = iptr->instid; /* reuse pre-reset instance ID */ + if (iptr->type == TRACE_TYPE_TRANSACT) tsortflag++; + else if (iptr->type == TRACE_TYPE_POINT) psortflag++; + else if (iptr->type == TRACE_TYPE_COUNTER) csortflag++; + else /*(iptr->type == TRACE_TYPE_OBSERVE)*/ osortflag++; + } + else if (type == TRACE_TYPE_TRANSACT) + newhash.id = ++transacts; + else if (type == TRACE_TYPE_POINT) + newhash.id = ++points; + else if (type == TRACE_TYPE_COUNTER) + newhash.id = ++counters; + else /* TRACE_TYPE_OBSERVE */ + newhash.id = ++observes; + newhash.txcount = -1; /* first time since reset or start */ + newhash.padding = 0; + newhash.realcount = 1; + newhash.realtime = data; + newhash.fd = clientfd; + newhash.txmin = newhash.txmax = data; + newhash.txsum = data; + hptr = &newhash; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "'%s' is new to the summary table!", + hptr->tag); +#endif + if (__pmhashinsert(&summary, hptr->tag, hptr) < 0) { + __pmNotifyErr(LOG_ERR, "summary table insert failure - '%s' " + "data ignored", hptr->tag); + free(hptr->tag); + return -1; + } + /* also update the instance domain */ + if (hptr->tracetype == TRACE_TYPE_TRANSACT) + indom = TRANSACT_INDOM; + else if (hptr->tracetype == TRACE_TYPE_POINT) + indom = POINT_INDOM; + else if (hptr->tracetype == TRACE_TYPE_COUNTER) + indom = COUNTER_INDOM; + else /*(hptr->tracetype == TRACE_TYPE_OBSERVE)*/ + indom = OBSERVE_INDOM; + +#ifdef DESPERATE + /* walk the indom table - if we find this new tag in it already, then + * something is badly busted. + */ + for (sts = 0; sts < indomtab[indom].it_numinst; sts++) { + if (strcmp(indomtab[indom].it_set[sts].i_name, hptr->tag) == 0) { + fprintf(stderr, "'%s' (inst=%d, type=%d) entry in indomtab already!!!\n", + hptr->tag, indomtab[indom].it_set[sts].i_inst, hptr->tracetype); + abort(); + } + } +#endif + + index = indomtab[indom].it_numinst; + size = (index+1)*(int)sizeof(pmdaInstid); + if ((indomtab[indom].it_set = (pmdaInstid *) + realloc(indomtab[indom].it_set, size)) == NULL) { + __pmNotifyErr(LOG_ERR, "dropping instance '%s': %s", hptr->tag, + osstrerror()); + free(hptr->tag); + return -1; + } + indomtab[indom].it_set[index].i_inst = hptr->id; + indomtab[indom].it_set[index].i_name = hptr->tag; + indomtab[indom].it_numinst++; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Updated indom #%d:\n", indom); + for (index=0; index < indomtab[indom].it_numinst; index++) + fprintf(stderr, " Instance %d='%s'\n", + indomtab[indom].it_set[index].i_inst, + indomtab[indom].it_set[index].i_name); + } +#endif + } + else { /* update an existing entry */ + freeflag++; /* wont need tag afterwards, so mark for deletion */ + if (hptr->taglength != newhash.taglength) { + __pmNotifyErr(LOG_ERR, "hash table update failure - '%s' " + "data ignored (bad tag length)", tag); + free(newhash.tag); + return -1; + } + else { /* update existing entries free running counter */ + hptr->realcount++; + if (hptr->tracetype == TRACE_TYPE_TRANSACT) + hptr->realtime += data; + /* keep running total of time attributed to transactions */ + else if (hptr->tracetype == TRACE_TYPE_COUNTER) + hptr->txsum = data; + /* counters are 'permanent' and immediately available */ + else if (hptr->tracetype == TRACE_TYPE_OBSERVE) + hptr->txsum = data; + /* observations are 'permanent' and immediately available */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "'%s' real count updated (%d)", + hptr->tag, (int)hptr->realcount); +#endif + } + } + + /* + * Next, update the current ring buffer entry with new trace data + */ + if ((hptr = __pmhashlookup(ringbuf.ring[rpos].stats, tag, + &newhash)) == NULL) { + hash.tag = strdup(tag); + hash.tracetype = type; + hash.id = 0; /* the ring buffer is never used to resolve indoms */ + hash.padding = 0; + hash.realcount = 1; + hash.realtime = data; + hash.taglength = (unsigned int)taglen; + hash.fd = clientfd; + hash.txcount = 1; + hash.txmin = hash.txmax = data; + hash.txsum = data; + hptr = &hash; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "fresh interval data on fd=%d rpos=%d " + "('%s': len=%d type=%d value=%d)", clientfd, rpos, + hash.tag, taglen, type, (int)data); +#endif + if (__pmhashinsert(ringbuf.ring[rpos].stats, hash.tag, hptr) < 0) { + __pmNotifyErr(LOG_ERR, "ring buffer insert failure - '%s' " + "data ignored", hash.tag); + free(hash.tag); + return -1; + } + } + else { /* update existing entry */ + hptr->txcount++; + if (hptr->tracetype == TRACE_TYPE_TRANSACT) { + if (data < hptr->txmin) + hptr->txmin = data; + if (data > hptr->txmax) + hptr->txmax = data; + hptr->txsum += data; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Updating data on fd=%d ('%s': type=%d " + "count=%d min=%f max=%f sum=%f)", + clientfd, hptr->tag, hptr->tracetype, + hptr->txcount, hptr->txmin, hptr->txmax, hptr->txsum); +#endif + } + if (freeflag) { + free(newhash.tag); + newhash.tag = NULL; + } + + return hptr->tracetype; +} + +static void +clearTable(hashtable_t *t, void *entry) +{ + hashdata_t *h = (hashdata_t *)entry; + h->txcount = -1; /* flag as out-of-date */ +} + +/* + * Goes off at set time interval. The old `tail' becomes the new `head' + * of the ring buffer & is marked as currently in-progess (and this data + * is not exported until the next timer event). + */ +void +timerUpdate(void) +{ + /* summary table must be reset for next fetch */ + if (dosummary == 0) { + __pmhashtraverse(&summary, clearTable); + dosummary = 1; + } + + if (ringbuf.ring[rpos].working == 0) { + __pmNotifyErr(LOG_ERR, "buffered I/O error - ignoring timer event"); + return; + } + + ringbuf.ring[rpos].working = 0; + if (rpos == rbufsize-1) + rpos = 0; /* return to start of buffer */ + else + rpos++; + __pmhashtrunc(ringbuf.ring[rpos].stats); /* new working set */ + ringbuf.ring[rpos].working = 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Alarm - working buffer is now %d/%d", + rpos+1, rbufsize); +#endif +} + +static void +summariseDataAux(hashtable_t *t, void *entry) +{ + hashdata_t *hptr; + hashdata_t *base = (hashdata_t *)entry; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "summariseDataAux: looking up '%s'", base->tag); +#endif + /* update summary hash table */ + if ((hptr = __pmhashlookup(&summary, base->tag, base)) == NULL) + __pmNotifyErr(LOG_ERR, "summariseDataAux: entry in ring buffer, " + "but not summary ('%s')", base->tag); + else { /* update an existing summary */ + if (hptr->tracetype == TRACE_TYPE_TRANSACT) { + if (hptr->txcount == -1) { /* reset coz flagged as out-of-date */ + tindomsize++; + hptr->txcount = base->txcount; + hptr->txmin = base->txmin; + hptr->txmax = base->txmax; + hptr->txsum = base->txsum; + } + else { + hptr->txcount += base->txcount; + if (base->txmin < hptr->txmin) + hptr->txmin = base->txmin; + if (base->txmax > hptr->txmax) + hptr->txmax = base->txmax; + hptr->txsum += base->txsum; + } + } + else { + if (hptr->txcount == -1) { /* reset coz flagged as out-of-date */ + if (hptr->tracetype == TRACE_TYPE_POINT) + pindomsize++; + else if (hptr->tracetype == TRACE_TYPE_COUNTER) + cindomsize++; + else if (hptr->tracetype == TRACE_TYPE_OBSERVE) + oindomsize++; + else { + __pmNotifyErr(LOG_ERR, + "bogus trace type - skipping '%s'", hptr->tag); + return; + } + hptr->txcount = base->txcount; + } + else { + hptr->txcount += base->txcount; + } + } + } +} + + +/* + * Create the summary hash table, as well as the instance list for + * this set of intervals. + */ +static void +summariseData(void) +{ + int count; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "summariseData: resummarising"); +#endif + /* initialise counters */ + tindomsize = pindomsize = oindomsize = 0; + + /* create the new summary table */ + for (count=0; count < rbufsize; count++) { + if (ringbuf.ring[count].working == 1) + continue; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "ring buffer table %d/%d has %d entries.\n", + count, rbufsize, ringbuf.ring[count].stats->entries); +#endif + __pmhashtraverse(ringbuf.ring[count].stats, summariseDataAux); + } + + /* summary now holds correct data for the rest of this interval */ + dosummary = 0; +} + +/* + * Try to keep trace fetching similar to libpcp_pmda pmdaFetch() + * we need a different way to get at the instances though + * so, we can still use __pmdaStartInst, but trace iterator + * is a bit different. + */ +static pmdaInstid * +nextTraceInst(pmdaExt *pmda) +{ + static pmdaInstid in = { PM_INDOM_NULL, NULL }; + pmdaInstid *iptr = NULL; + + if (pmda->e_singular == 0) { + /* PM_INDOM_NULL ... just the one value */ + iptr = ∈ + pmda->e_singular = -1; + } + if (pmda->e_ordinal >= 0) { + int j; + for (j = pmda->e_ordinal; j < pmda->e_idp->it_numinst; j++) { + if (__pmInProfile(pmda->e_idp->it_indom, pmda->e_prof, + pmda->e_idp->it_set[j].i_inst)) { + iptr = &pmda->e_idp->it_set[j]; + pmda->e_ordinal = j+1; + break; + } + } + } + return iptr; +} + +static int +auxFetch(int inst, __pmID_int *idp, char *tag, pmAtomValue *atom) +{ + if (inst != PM_IN_NULL && idp->cluster != 0) + return PM_ERR_INST; + + /* transaction, point, counter and observe trace values and control data */ + if (idp->cluster == 0) { + hashdata_t hash; + hashdata_t *hptr; + + hash.tag = tag; + + switch (idp->item) { + case 0: /* trace.transact.count */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->ull = hptr->realcount; + break; + case 1: /* trace.transact.rate */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)((double)hptr->txcount/(double)timespan.tv_sec); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "rate=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txcount, (double)timespan.tv_sec, tag); +#endif + break; + case 2: /* trace.transact.ave_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + else if (hptr->txcount == 0) + atom->f = 0; + else { + atom->f = (float)((double)hptr->txsum/(double)hptr->txcount); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "ave_time=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txsum, (double)hptr->txcount, tag); +#endif + } + break; + case 3: /* trace.transact.min_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)hptr->txmin; + break; + case 4: /* trace.transact.max_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)hptr->txmax; + break; + case 5: /* trace.transact.total_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->d = hptr->realtime; + break; + case 6: /* trace.point.count */ + hash.tracetype = TRACE_TYPE_POINT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->ull = hptr->realcount; + break; + case 7: /* trace.point.rate */ + hash.tracetype = TRACE_TYPE_POINT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)((double)hptr->txcount/(double)timespan.tv_sec); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "rate=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txcount, (double)timespan.tv_sec, tag); +#endif + break; + case 8: /* trace.observe.count */ + case 17: /* trace.counter.count */ + hash.tracetype = (idp->item == 8)? + TRACE_TYPE_OBSERVE : TRACE_TYPE_COUNTER; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->ull = hptr->realcount; + break; + case 9: /* trace.observe.rate */ + case 18: /* trace.counter.rate */ + hash.tracetype = (idp->item == 9)? + TRACE_TYPE_OBSERVE : TRACE_TYPE_COUNTER; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)((double)hptr->txcount/(double)timespan.tv_sec); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "rate=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txcount, (double)timespan.tv_sec, tag); +#endif + break; + case 10: /* trace.observe.value */ + case 19: /* trace.counter.value */ + hash.tracetype = (idp->item == 10)? + TRACE_TYPE_OBSERVE : TRACE_TYPE_COUNTER; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->d = hptr->txsum; + break; + case 11: /* trace.control.timespan */ + atom->ul = timespan.tv_sec; + break; + case 12: /* trace.control.interval */ + atom->ul = interval.tv_sec; + break; + case 13: /* trace.control.buckets */ + atom->ul = rbufsize-1; + break; + case 14: /* trace.control.port */ + atom->ul = ctlport; + break; + case 15: /* trace.control.reset */ + atom->ul = 1; + break; + case 16: /* trace.control.debug */ + atom->ul = pmDebug; + break; + default: + return PM_ERR_PMID; + } + } + else + return PM_ERR_PMID; + + return 1; +} + +static int +getIndomSize(__pmID_int *pmidp) +{ + int size; + + if (pmidp->cluster != 0) + return 1; + + switch (pmidp->item) { + case 0: /* uses summary's real counters (transact) */ + case 5: + size = indomtab[TRANSACT_INDOM].it_numinst; + break; + case 1: + case 2: + case 3: + case 4: /* susceptible to ring buffer updates */ + size = tindomsize; + break; + case 6: /* uses summary's real counters (point) */ + size = indomtab[POINT_INDOM].it_numinst; + break; + case 7: /* susceptible to ring buffer updates */ + size = pindomsize; + break; + case 8: + case 10: /* uses summary's real counters & data (obs) */ + size = indomtab[OBSERVE_INDOM].it_numinst; + break; + case 9: /* susceptible to ring buffer updates */ + size = oindomsize; + break; + case 17: + case 19: /* uses summary's real counters & data (ctr) */ + size = indomtab[COUNTER_INDOM].it_numinst; + break; + case 18: /* susceptible to ring buffer updates */ + size = cindomsize; + break; + default: + size = 0; + } + return size; +} + + +static int +traceFetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + static int maxnpmids = 0; + static pmResult *res = NULL; + pmValueSet *vset; + pmDesc *dp; + __pmID_int *pmidp; + pmdaMetric *metap; + pmAtomValue atom; + pmdaInstid *ins; + pmdaInstid noinst = { PM_IN_NULL, NULL }; + int numval; + int sts, i, j, need; + + indomSortCheck(); + pmda->e_idp = indomtab; + + if (numpmid > maxnpmids) { + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = (int)sizeof(pmResult) + (numpmid-1)*(int)sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == NULL) { + return -oserror(); + } + maxnpmids = numpmid; + } + + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + for (i = 0; i < numpmid; i++) { + dp = NULL; + metap = NULL; + pmidp = (__pmID_int *)&pmidlist[i]; + if (pmda->e_direct) { + if (pmidp->item < pmda->e_nmetrics && + pmidlist[i] == pmda->e_metrics[pmidp->item].m_desc.pmid) { + metap = &pmda->e_metrics[pmidp->item]; + dp = &(metap->m_desc); + } + } + else { /* search for it */ + for (j = 0; j < pmda->e_nmetrics; j++) { + if (pmidlist[i] == pmda->e_metrics[j].m_desc.pmid) { + metap = &pmda->e_metrics[j]; + dp = &(metap->m_desc); + break; + } + } + } + if (dp == NULL) { + __pmNotifyErr(LOG_ERR, "traceFetch: Requested metric %s is not " + "defined", pmIDStr(pmidlist[i])); + numval = PM_ERR_PMID; + } + else if (dp->indom != PM_INDOM_NULL) { + /* + * Only summarise when you have to, so check if data has + * already been summarised within this interval. + */ + if (dosummary == 1) + summariseData(); + numval = getIndomSize(pmidp); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "instance domain for %s numval=%d", + pmIDStr(dp->pmid), numval); +#endif + } + else + numval = 1; + + /* Must use individual malloc()s because of pmFreeResult() */ + if (numval >= 1) + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)+ + (numval - 1)*sizeof(pmValue)); + else + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)- + sizeof(pmValue)); + if (vset == NULL) { + if ((res->numpmid = i) > 0) + __pmFreeResultValues(res); + return -oserror(); + } + + vset->pmid = pmidlist[i]; + vset->numval = numval; + vset->valfmt = PM_VAL_INSITU; + if (vset->numval <= 0) + continue; + + if (dp->indom == PM_INDOM_NULL) + ins = &noinst; + else { + __pmdaStartInst(dp->indom, pmda); + ins = nextTraceInst(pmda); + } + j = 0; + do { + if (ins == NULL) { + __pmNotifyErr(LOG_ERR, "bogus instance ignored (pmid=%s)", + pmIDStr(dp->pmid)); + if ((res->numpmid = i) > 0) + __pmFreeResultValues(res); + return PM_ERR_INST; + } + if (j == numval) { + numval++; + res->vset[i] = vset = (pmValueSet *)realloc(vset, + sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue)); + if (vset == NULL) { + if ((res->numpmid = i) > 0) + __pmFreeResultValues(res); + return -oserror(); + } + } + vset->vlist[j].inst = ins->i_inst; + if ((sts = auxFetch(ins->i_inst, pmidp, ins->i_name, &atom)) < 0) { + if (sts == PM_ERR_PMID) + __pmNotifyErr(LOG_ERR, "unknown PMID requested - '%s'", + pmIDStr(dp->pmid)); + else if (sts == PM_ERR_INST) + __pmNotifyErr(LOG_ERR, "unknown instance requested - %d " + "(pmid=%s)", ins->i_inst, pmIDStr(dp->pmid)); + else + __pmNotifyErr(LOG_ERR, "fetch error (pmid=%s): %s", + pmIDStr(dp->pmid), pmErrStr(sts)); + } + else if (sts == 0) { /* not current, so don't use */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Instance is dated %s (pmid=%s)", + ins->i_name, pmIDStr(dp->pmid)); +#endif + } + else if ((sts = __pmStuffValue(&atom, &vset->vlist[j], dp->type)) == PM_ERR_TYPE) + __pmNotifyErr(LOG_ERR, "bad desc type (%d) for metric %s", + dp->type, pmIDStr(dp->pmid)); + else if (sts >= 0) { + vset->valfmt = sts; + j++; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Instance is good! %s (pmid=%s)", + ins->i_name, pmIDStr(dp->pmid)); +#endif + } + } while (dp->indom != PM_INDOM_NULL && + (ins = nextTraceInst(pmda)) != NULL); + if (j == 0) + vset->numval = sts; + else + vset->numval = j; + } + *resp = res; + + return 0; +} + + +static int +traceStore(pmResult *result, pmdaExt *pmda) +{ + int i, j; + int sts = 0; + pmValueSet *vsp = NULL; + __pmID_int *pmidp = NULL; + pmAtomValue av; + extern int afid; + extern void alarming(int, void *); + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster != 0) + return PM_ERR_PMID; + + if (pmidp->item == 15) { /* trace.control.reset */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "resetting trace metrics"); +#endif + /* reset the interval timer */ + if (afid >= 0) { + __pmAFunregister(afid); + if ((afid = __pmAFregister(&interval, NULL, alarming)) < 0) { + __pmNotifyErr(LOG_ERR, "__pmAFregister failed"); + exit(1); + } + } + + /* reset summary and ring buffer hash tables */ + __pmhashtrunc(&summary); + for (j = 0; j < rbufsize; j++) { + __pmhashtrunc(ringbuf.ring[j].stats); + ringbuf.ring[j].numstats = 0; + } + + /* clear all the instance domain entries */ + if (indomtab[TRANSACT_INDOM].it_set) { + free(indomtab[TRANSACT_INDOM].it_set); + indomtab[TRANSACT_INDOM].it_set = NULL; + } + if (indomtab[OBSERVE_INDOM].it_set) { + free(indomtab[OBSERVE_INDOM].it_set); + indomtab[OBSERVE_INDOM].it_set = NULL; + } + if (indomtab[COUNTER_INDOM].it_set) { + free(indomtab[COUNTER_INDOM].it_set); + indomtab[COUNTER_INDOM].it_set = NULL; + } + if (indomtab[POINT_INDOM].it_set) { + free(indomtab[POINT_INDOM].it_set); + indomtab[POINT_INDOM].it_set = NULL; + } + indomtab[TRANSACT_INDOM].it_numinst = 0; + indomtab[COUNTER_INDOM].it_numinst = 0; + indomtab[OBSERVE_INDOM].it_numinst = 0; + indomtab[POINT_INDOM].it_numinst = 0; + tindomsize = pindomsize = oindomsize = 0; + /* definately need to recompute the summary next fetch */ + dosummary = 1; + __pmNotifyErr(LOG_INFO, "PMDA reset"); + } + else if (pmidp->item == 16) { /* trace.control.debug */ + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_INSITU) + sts = PM_ERR_CONV; + else if (sts >= 0 && ((sts = pmExtractValue(vsp->valfmt, + &vsp->vlist[0], PM_TYPE_32, &av, PM_TYPE_32)) >= 0)) { + if (pmDebug != av.l) { + pmDebug = av.l; + __pmNotifyErr(LOG_INFO, "debug level set to %d", pmDebug); + debuglibrary(pmDebug); + } + } + } + else + sts = PM_ERR_PMID; + } + return sts; +} + + +/* + * Initialise the agent + */ +void +traceInit(pmdaInterface *dp) +{ + int rsize, sts; + + if (dp->status != 0) + return; + + dp->version.two.fetch = traceFetch; + dp->version.two.store = traceStore; + dp->version.two.instance = traceInstance; + dp->version.two.ext->e_direct = 0; + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); + + /* initialise ring buffer */ + rsize = (int)(sizeof(statlist_t) * rbufsize); + if ((ringbuf.ring = (statlist_t *)malloc(rsize)) == NULL) { + __pmNotifyErr(LOG_ERR, "failed during ring buffer initialise: %s", + osstrerror()); + exit(1); + } + for (rsize=0; rsize < rbufsize; rsize++) { + if ((ringbuf.ring[rsize].stats = (hashtable_t *) + malloc(sizeof(hashtable_t))) == NULL) { + __pmNotifyErr(LOG_ERR, "ring buffer initialise failed: %s", + osstrerror()); + exit(1); + } + if ((sts = __pmhashinit(ringbuf.ring[rsize].stats, 0, sizeof(hashdata_t), + datacmp, datadel)) < 0) { + __pmNotifyErr(LOG_ERR, "ring buffer initialisation failed: %s", + osstrerror()); + exit(1); + } + ringbuf.ring[rsize].working = 0; + } + rpos = 0; + ringbuf.ring[rpos].working = 1; + + /* initialise summary & associated instance domain */ + indomtab[TRANSACT_INDOM].it_numinst = 0; + indomtab[TRANSACT_INDOM].it_set = NULL; + indomtab[POINT_INDOM].it_numinst = 0; + indomtab[POINT_INDOM].it_set = NULL; + dp->version.two.ext->e_idp = indomtab; + if ((sts = __pmhashinit(&summary, 0, sizeof(hashdata_t), + datacmp, summarydel)) < 0) { + __pmNotifyErr(LOG_ERR, "summary table initialisation failed: %s", + osstrerror()); + exit(1); + } + /* initialise list of reserved instance domains (for store recovery) */ + if ((sts = __pmhashinit(&history, 0, sizeof(instdata_t), + instcmp, instdel)) < 0) { + __pmNotifyErr(LOG_ERR, "history table initialisation failed: %s", + osstrerror()); + exit(1); + } +} diff --git a/src/pmdas/trace/stub.c b/src/pmdas/trace/stub.c new file mode 100644 index 0000000..08790be --- /dev/null +++ b/src/pmdas/trace/stub.c @@ -0,0 +1,136 @@ +/* + * stub.c - libpcp_trace stubs + * + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include + +int __pmstate = 0; + +int +pmtracebegin(const char *tag) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtracebegin: start of transaction '%s'\n", tag); +#endif + return 0; +} + +int +pmtraceend(const char *tag) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtraceend: end of transaction '%s'\n", tag); +#endif + return 0; +} + +int +pmtraceabort(const char *tag) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtraceabort: transaction '%s' aborted\n", tag); +#endif + return 0; +} + +int +pmtraceobs(const char *label, double value) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtraceobs: observation '%s', value=%f\n", label, value); +#endif + return 0; +} + +int +pmtracecounter(const char *label, double value) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtracecounter: counter '%s', value=%f\n", label, value); +#endif + return 0; +} + +int +pmtracepoint(const char *label) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtracepoint: trace point '%s' reached\n", label); +#endif + return 0; +} + +int +pmtracestate(int code) +{ + return(__pmstate |= code); +} + +char * +pmtraceerrstr(int code) +{ + static const struct { + int code; + char *msg; + } errtab[] = { + { PMTRACE_ERR_TAGNAME, + "Invalid tag name - tag names cannot be NULL" }, + { PMTRACE_ERR_INPROGRESS, + "Transaction is already in progress - cannot begin" }, + { PMTRACE_ERR_NOPROGRESS, + "Transaction is not currently in progress - cannot end" }, + { PMTRACE_ERR_NOSUCHTAG, + "Transaction tag was not successfully initialised" }, + { PMTRACE_ERR_TAGTYPE, + "Tag is already in use for a different type of tracing" }, + { PMTRACE_ERR_TAGLENGTH, + "Tag name is too long (maximum 256 characters)" }, + { PMTRACE_ERR_IPC, + "IPC protocol failure" }, + { PMTRACE_ERR_ENVFORMAT, + "Unrecognised environment variable format" }, + { PMTRACE_ERR_TIMEOUT, + "Application timed out connecting to the PMDA" }, + { PMTRACE_ERR_VERSION, + "Incompatible versions between application and PMDA" }, + { PMTRACE_ERR_PERMISSION, + "Cannot connect to PMDA - permission denied" }, + { PMTRACE_ERR_CONNLIMIT, + "Cannot connect to PMDA - connection limit reached" }, + { 0, "" } + }; + + if ((code < 0) && (code > -PMTRACE_ERR_BASE)) /* catch intro(2) errors */ + return strerror(-code); + else if (code == 0) + return "No error"; + else { + int i; + for (i=0; errtab[i].code; i++) { + if (errtab[i].code == code) + return errtab[i].msg; + } + } + return "Unknown error code"; +} diff --git a/src/pmdas/trivial/GNUmakefile b/src/pmdas/trivial/GNUmakefile new file mode 100644 index 0000000..dfa9d8c --- /dev/null +++ b/src/pmdas/trivial/GNUmakefile @@ -0,0 +1,43 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +DFILES = README +CFILES = trivial.c +CMDTARGET = pmdatrivial$(EXECSUFFIX) +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = -I. +LSRCFILES = Install Remove pmns help $(DFILES) root GNUmakefile.install + +IAM = trivial +DOMAIN = TRIVIAL +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so + +default_pcp default: domain.h $(CMDTARGET) + +include $(BUILDRULES) + +install install_pcp: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 GNUmakefile.install $(PMDADIR)/Makefile + $(INSTALL) -m 644 $(DFILES) root help pmns domain.h $(CFILES) $(PMDADIR) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/trivial/GNUmakefile.install b/src/pmdas/trivial/GNUmakefile.install new file mode 100644 index 0000000..92f1351 --- /dev/null +++ b/src/pmdas/trivial/GNUmakefile.install @@ -0,0 +1,51 @@ +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +SHELL = sh + +ifdef PCP_CONF +include $(PCP_CONF) +else +include $(PCP_DIR)/etc/pcp.conf +endif +include $(PCP_INC_DIR)/builddefs + +# remove -Lpath and -Ipath options from builddefs CFLAGS value +# +PCP_LIBS = +TMP := $(CFLAGS:-I%=) +ifdef PCP_DIR +# put -Ipath and -Lpath back but use paths for run-time environment +# +CFLAGS = $(TMP) -I$(PCP_INC_DIR)/.. +LDFLAGS = -L$(PCP_LIB_DIR) +else +CFLAGS = $(TMP) +endif + +IAM = trivial +CFILES = $(IAM).c + +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +CMDTARGET = pmda$(IAM) +TARGETS = $(LIBTARGET) $(CMDTARGET) + +LLDLIBS = -lpcp_pmda -lpcp $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) +LDIRT = *.log help.dir help.pag + +default: $(TARGETS) + +install: default + +include $(PCP_INC_DIR)/buildrules diff --git a/src/pmdas/trivial/Install b/src/pmdas/trivial/Install new file mode 100644 index 0000000..02fb13a --- /dev/null +++ b/src/pmdas/trivial/Install @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the trivial PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=trivial +pmda_interface=2 +forced_restart=false + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/trivial/README b/src/pmdas/trivial/README new file mode 100644 index 0000000..9561cc6 --- /dev/null +++ b/src/pmdas/trivial/README @@ -0,0 +1,63 @@ +Trivial PMDA +============ + +This PMDA is a sample that illustrates how a simple PMDA might be +constructed using libpcp_pmda. + +Although the metrics supported as simple, the framework is quite +general, and could be extended to implement a much more complex PMDA. + +Note: + This PMDA may be remade from source and hence requires IDO (or + more specifically a C compiler) to be installed. + + Uses of make(1) may fail (without removing or clobbering files) + if the C compiler cannot be found. This is most likely to + happen when running the PMDA ./Install script. + + The only remedial action is to install the C compiler, or + hand-craft changes to the Makefile. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT trivial + +Installation +============ + + + # cd $PCP_PMDAS_DIR/trivial + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/trivial + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/trivial.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/trivial/Remove b/src/pmdas/trivial/Remove new file mode 100644 index 0000000..7de9372 --- /dev/null +++ b/src/pmdas/trivial/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the trivial PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=trivial + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/trivial/help b/src/pmdas/trivial/help new file mode 100644 index 0000000..300bcd4 --- /dev/null +++ b/src/pmdas/trivial/help @@ -0,0 +1,35 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# trivial PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ trivial.time The time in seconds since 1 Jan 1970 +The time in seconds since the 1st of January, 1970. diff --git a/src/pmdas/trivial/pmns b/src/pmdas/trivial/pmns new file mode 100644 index 0000000..f7a6167 --- /dev/null +++ b/src/pmdas/trivial/pmns @@ -0,0 +1,23 @@ +/* + * Metrics for trivial PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +trivial { + time TRIVIAL:0:0 +} diff --git a/src/pmdas/trivial/root b/src/pmdas/trivial/root new file mode 100644 index 0000000..579f567 --- /dev/null +++ b/src/pmdas/trivial/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { trivial } + +#include "pmns" + diff --git a/src/pmdas/trivial/trivial.c b/src/pmdas/trivial/trivial.c new file mode 100644 index 0000000..b395538 --- /dev/null +++ b/src/pmdas/trivial/trivial.c @@ -0,0 +1,138 @@ +/* + * Trivial, configurable PMDA + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include "domain.h" + +/* + * Trivial PMDA + * + * This PMDA is a sample that illustrates how a trivial PMDA might be + * constructed using libpcp_pmda. + * + * Although the metrics supported are trivial, the framework is quite general, + * and could be extended to implement a much more complex PMDA. + * + * Metrics + * trivial.time - time in seconds since the 1st of Jan, 1970. + */ + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +static pmdaMetric metrictab[] = { +/* time */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } } +}; + +static char *username; +static char mypath[MAXPATHLEN]; +static int isDSO = 1; /* ==0 if I am a daemon */ + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; +static pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +/* + * callback provided to pmdaFetch + */ +static int +trivial_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (idp->cluster != 0 || idp->item != 0) + return PM_ERR_PMID; + else if (inst != PM_IN_NULL) + return PM_ERR_INST; + + atom->ul = time(NULL); + return 0; +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +trivial_init(pmdaInterface *dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "trivial" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_2, "trivial DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + pmdaSetFetchCallBack(dp, trivial_fetchCallBack); + + pmdaInit(dp, NULL, 0, + metrictab, sizeof(metrictab)/sizeof(metrictab[0])); +} + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface desc; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "trivial" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, TRIVIAL, + "trivial.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &desc); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&desc); + trivial_init(&desc); + pmdaConnect(&desc); + pmdaMain(&desc); + + exit(0); +} diff --git a/src/pmdas/txmon/GNUmakefile b/src/pmdas/txmon/GNUmakefile new file mode 100644 index 0000000..7c423ba --- /dev/null +++ b/src/pmdas/txmon/GNUmakefile @@ -0,0 +1,68 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +DFILES = README +CFILES = txmon.c txrecord.c +HFILES = txmon.h +TARGETS = txmon$(EXECSUFFIX) txrecord$(EXECSUFFIX) + +LCFLAGS = -I. +LLDLIBS = $(PCP_PMDALIB) + +SCRIPTS = Install Remove genload +OTHERS = pmns help root +LSRCFILES= $(SCRIPTS) $(OTHERS) GNUmakefile.install $(DFILES) + +IAM = txmon +DOMAIN = TXMON +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = *.o *.log *.dir *.pag domain.h $(TARGETS) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(TARGETS) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root $(HFILES) $(PMDADIR) + $(INSTALL) -m 644 help pmns domain.h $(CFILES) $(PMDADIR) + $(INSTALL) -m 644 GNUmakefile.install $(PMDADIR)/Makefile +else +build-me: +install: +endif + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(OBJECTS): domain.h + +txmon$(EXECSUFFIX): txmon.o + $(CCF) -o $@ $(LDFLAGS) txmon.o $(LDLIBS) + +txrecord$(EXECSUFFIX): txrecord.o + $(CCF) -o $@ $(LDFLAGS) txrecord.o $(LDLIBS) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/txmon/GNUmakefile.install b/src/pmdas/txmon/GNUmakefile.install new file mode 100644 index 0000000..1f264c0 --- /dev/null +++ b/src/pmdas/txmon/GNUmakefile.install @@ -0,0 +1,57 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2000,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +SHELL = sh + +ifdef PCP_CONF +include $(PCP_CONF) +else +PCP_DIR = $(shell echo $$PCP_DIR) +include $(PCP_DIR)/etc/pcp.conf +endif +include $(PCP_INC_DIR)/builddefs + +# remove -Lpath and -Ipath options from builddefs CFLAGS value +# +PCP_LIBS = +TMP := $(CFLAGS:-I%=) +ifdef PCP_DIR +# put -Ipath and -Lpath back but use paths for run-time environment +# +CFLAGS = $(TMP) -I$(PCP_INC_DIR)/.. +LDFLAGS = -L$(PCP_LIB_DIR) +else +CFLAGS = $(TMP) +endif + +IAM = txmon +CFILES = $(IAM).c +CMDTARGET = pmda$(IAM) +TARGETS = txrecord $(CMDTARGET) +LLDLIBS = -lpcp_pmda -lpcp $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) +LDIRT = *.log help.dir help.pag txrecord + +default: $(TARGETS) + +install: default + +pmda$(IAM): txmon.h domain.h $(CFILES) + +txrecord.o: txmon.h txrecord.c + +txrecord: txrecord.o + $(CC) $(CFLAGS) -o $@ txrecord.o $(LDFLAGS) $(LDLIBS) + +include $(PCP_INC_DIR)/buildrules diff --git a/src/pmdas/txmon/Install b/src/pmdas/txmon/Install new file mode 100755 index 0000000..b8e1fcf --- /dev/null +++ b/src/pmdas/txmon/Install @@ -0,0 +1,56 @@ +#! /bin/sh +# +# Copyright (c) 1997,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the txmon PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=txmon +pmda_interface=2 +forced_restart=false + +pmdaSetup + +if $do_pmda +then + # special txmon PMDA args + # + echo 'Welcome to the Install script for the demonstration "txmon" PMDA. +This PMDA will establish a shared memory segment with one statistics +structure per transaction type. + +You must define the names of the transaction types (the names are +arbitrary strings with no embedded white space, e.g. mytx#1). +' + + args="" + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N "Name for transaction type? [return if no more] ""$PCP_ECHO_C" + read ans + [ -z "$ans" ] && break + args="$args $ans" + done + if [ -z "$args" ] + then + echo "You need to specify at least one transaction type name!" + exit 1 + fi +fi + +pmdaInstall + +exit 0 diff --git a/src/pmdas/txmon/README b/src/pmdas/txmon/README new file mode 100644 index 0000000..97fea32 --- /dev/null +++ b/src/pmdas/txmon/README @@ -0,0 +1,77 @@ +txmon PMDA +=========== + +This PMDA is a sample that illustrates how a simple transaction monitor +PMDA might be constructed, using a shared memory segment to transfer +information about transaction activity from the applications that +submit (or process) transactions and the txmon PMDA. + +Although the metrics supported are simple, the framework is quite +general and could be extended to implement a much more complex PMDA. + +Note: + This PMDA may be remade from source and hence requires IDO (or + more specifically a C compiler) to be installed. + + Uses of make(1) may fail (without removing or clobbering files) + if the C compiler cannot be found. This is most likely to + happen when running the PMDA ./Install script. + + The only remedial action is to install the C compiler, or + hand-craft changes to the Makefile. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT txmon + +Installation +============ + + + # cd $PCP_PMDAS_DIR/txmon + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + You will be prompted to define the names of the transaction types + to be monitored -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/txmon + # ./Remove + +Making something happen +======================= + +The application ./txrecord updates the shared memory segment to add +new information about transactions and their service times. Usually +this would be run from the ./genload script that will continue to +update the shared memory segment with data drawn from some synthetic +distributions. + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/txmon.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/txmon/Remove b/src/pmdas/txmon/Remove new file mode 100644 index 0000000..5b71f70 --- /dev/null +++ b/src/pmdas/txmon/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the txmon PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=txmon + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/txmon/genload b/src/pmdas/txmon/genload new file mode 100755 index 0000000..d906eaa --- /dev/null +++ b/src/pmdas/txmon/genload @@ -0,0 +1,120 @@ +#! /bin/sh +# +# Copyright (c) 1997,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# micky mouse transaction load generator ... +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + + +NUMTX=-1 +while getopts n:\? c +do + case $c + in + n) NUMTX=$OPTARG;; + \?) echo "Usage: genload [-n numtx] [tps [avg_serv]]" + exit 1 + ;; + esac +done +shift `expr $OPTIND - 1` + +# target TPS [default 10] +# +TPS=${1-10} + +# mean of the mean service times [default 10 seconds] +# +AVG=${2-10} + +if [ ! -x ./txrecord ] +then + echo 'Error: no txrecord binary here ... try "make txrecord"?' + exit 1 +fi + +# get tx types from txmon PMDA via pmcd on the localhost, then choose tx +# type with Zipfian distribution, and artifically chosen service times +# ... pipe them to a record-then-sleep droid in the while loop at the +# end ... rate throttling relies on O/S pipe-full synchronization +# (crude, but simple and effective) +# +pminfo -f txmon.count \ +| tr -d '"\]\[' \ +| $PCP_AWK_PROG ' +BEGIN { i = 0; w = 2; s = 0; numtx='$NUMTX' } +/value/ { name[i] = $4; zipf[i] = s + 1 / w; s = zipf[i] + i++; w *= 2 + } +END { if (i == 0) { + print "e Cannot determine transaction type names ... is the txmon PMDA running locally?" + exit + } + # compute mean, drawn from normal distn with mean and std dev AVG + for (j=0; j= -0.69314706) x = -x + m[j] = '$AVG' + '$AVG' * x + if (m[j] >= 0) + break + } + } + print "i Mean service time for " name[j] " tx: " m[j] + } + zipf[i-1] = 1 # force upper bound of last interval + while (numtx != 0) { + printf "w " + for (k=0; k < '$TPS' && numtx != 0; k++) { + r = rand() + for (j=0; j 0) + numtx-- + } + print "" + print "s" + } + }' \ +| while read action arg +do + case $action + in + i) echo "$arg" + ;; + e) echo "Error: $arg" + exit 1 + ;; + s) sleep 1 + ;; + w) ./txrecord $arg + ;; + esac +done + + diff --git a/src/pmdas/txmon/help b/src/pmdas/txmon/help new file mode 100644 index 0000000..83e2300 --- /dev/null +++ b/src/pmdas/txmon/help @@ -0,0 +1,62 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# txmon PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ TXMON.0 Instance domain "transaction type name" for txmon PMDA +There is one instance for each transaction type. + +@ txmon.count count by transaction type +Count by transaction type of transactions serviced since the txmon PMDA +started. + +@ txmon.reset_count reset count by transaction type +Count by transaction type of transactions serviced since the last time +the average and maximum summaries were reset. + +@ txmon.ave_time average time per transaction type +Average recorded service time per transaction type since the last time +the statistics were reset. + +@ txmon.max_time maximum time per transaction type +Maximum recorded service time per transaction type since the last time +the statistics were reset. + +@ txmon.control.level statistics control levels +0 => no statistics are collected +1 => transaction counts are enabled +2 => transaction counts and service times are enabled + +@ txmon.control.reset reset statistics +This metric is a toggle-switch ... any pmStore() operation for this +metric will reset all statistics except txmon.count. + +The value for this metric is always 1. diff --git a/src/pmdas/txmon/pmns b/src/pmdas/txmon/pmns new file mode 100644 index 0000000..9535a4d --- /dev/null +++ b/src/pmdas/txmon/pmns @@ -0,0 +1,32 @@ +/* + * Metrics for txmon PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +txmon { + count TXMON:0:0 + ave_time TXMON:0:1 + max_time TXMON:0:2 + reset_count TXMON:0:3 + control +} + +txmon.control { + level TXMON:0:4 + reset TXMON:0:5 +} diff --git a/src/pmdas/txmon/root b/src/pmdas/txmon/root new file mode 100644 index 0000000..3d85304 --- /dev/null +++ b/src/pmdas/txmon/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { txmon } + +#include "pmns" + diff --git a/src/pmdas/txmon/txmon.c b/src/pmdas/txmon/txmon.c new file mode 100644 index 0000000..bdfbf7a --- /dev/null +++ b/src/pmdas/txmon/txmon.c @@ -0,0 +1,383 @@ +/* + * txmon PMDA + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include "domain.h" + +#define CACHELINE 32 +#define RND_TO_CACHE_LINE(x) (((x + CACHELINE - 1) / CACHELINE) * CACHELINE) + +/* + * txmon PMDA + * + * This PMDA is a sample that illustrates how a PMDA might be + * constructed with libpcp_pmda to use a System V shared memory (shm) + * segment to transfer performance data between the collectors on the + * application side (see ./txrecord and ./genload for a simple example) + * and this PCP PMDA + */ + +/* + * list of instances ... created dynamically, after parsing cmd line options + */ +static pmdaInstid *tx_indom; + +/* + * list of instance domains ... initialized after parsing cmd line options + */ +static pmdaIndom indomtab[] = { +#define TX_INDOM 0 + { TX_INDOM, 0, NULL }, +}; + +/* + * all metrics supported in this PMDA - one table entry for each + */ +static pmdaMetric metrictab[] = { +/* count */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, TX_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* ave_time */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_FLOAT, TX_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,-1,0,PM_TIME_SEC,PM_COUNT_ONE) } }, +/* max_time */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_FLOAT, TX_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0 ) } }, +/* reset_count */ + { NULL, + { PMDA_PMID(0,3), PM_TYPE_U32, TX_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, +/* control.level */ + { NULL, + { PMDA_PMID(0,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, +/* control.reset */ + { NULL, + { PMDA_PMID(0,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) } }, +}; + +#include "./txmon.h" + +static int shmid = -1; + +static char mypath[MAXPATHLEN]; +static char *username; + +/* + * callback provided to pmdaFetch + */ +static int +txmon_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + stat_t *sp; + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + unsigned int real_count; + + if (inst != PM_IN_NULL && mdesc->m_desc.indom == PM_INDOM_NULL) + return PM_ERR_INST; + + if (idp->cluster != 0) + return PM_ERR_PMID; + + if (idp->item <= 3) { + if (inst >= control->n_tx) + return PM_ERR_INST; + + sp = (stat_t *)((__psint_t)control + control->index[inst]); + + switch (idp->item) { + case 0: /* txmon.count */ + if (control->level < 1) + return PM_ERR_AGAIN; + atom->ul = sp->count; + break; + case 1: /* txmon.ave_time */ + if (control->level < 2) + return PM_ERR_AGAIN; + real_count = sp->count - sp->reset_count; + atom->f = real_count > 0 ? sp->sum_time / real_count : -1; + break; + case 2: /* txmon.max_time */ + if (control->level < 2) + return PM_ERR_AGAIN; + atom->f = sp->max_time; + break; + case 3: /* txmon.reset_count */ + if (control->level < 1) + return PM_ERR_AGAIN; + atom->ul = sp->count - sp->reset_count; + break; + } + } + else { + switch (idp->item) { + + case 4: /* txmon.control.level */ + atom->ul = control->level; + break; + case 5: /* txmon.control.reset */ + atom->ul = 1; + break; + default: + return PM_ERR_PMID; + } + } + + return 0; +} + +/* + * support the storage of a value into the control metrics + */ +static int +txmon_store(pmResult *result, pmdaExt *pmda) +{ + int i; + int n; + int val; + int sts = 0; + pmValueSet *vsp = NULL; + __pmID_int *pmidp = NULL; + stat_t *sp; + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster == 0) { /* all storable metrics are cluster 0 */ + + switch (pmidp->item) { + case 0: /* no store for these ones */ + case 1: + case 2: + case 3: + sts = PM_ERR_PERMISSION; + break; + + case 4: /* txmon.control.level */ + val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 0; + } + control->level = val; + break; + + case 5: /* txmon.control.reset */ + for (n = 0; n < control->n_tx; n++) { + sp = (stat_t *)((__psint_t)control + control->index[n]); + sp->reset_count = sp->count; + sp->sum_time = 0; + sp->max_time = -1; + } + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else + sts = PM_ERR_PMID; + } + return sts; +} + + +/* + * Initialise the agent + */ +void +txmon_init(pmdaInterface *dp) +{ + if (dp->status != 0) + return; + + __pmSetProcessIdentity(username); + + dp->version.two.store = txmon_store; + + pmdaSetFetchCallBack(dp, txmon_fetchCallBack); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "after pmdaSetFetchCallBack() control @ %p\n", control); + } +#endif + + pmdaInit(dp, indomtab, 1, metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "after pmdaInit() control @ %p\n", control); + } +#endif +} + +/* + * come here on exit() + */ +static void +done(void) +{ + if (shmid != -1) + /* remove the shm segment */ + shmctl(shmid, IPC_RMID, NULL); +} + +pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, + .short_usage = "[options] tx_type [...]", +}; + +/* + * Set up the agent. + */ +int +main(int argc, char **argv) +{ + int n, sep = __pmPathSeparator(); + char *p; + pmdaInterface dispatch; + size_t index_size; + size_t shm_size; + stat_t *sp; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "txmon" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, TXMON, + "txmon.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + n = argc - opts.optind; + if (n < 1) { + pmprintf("No transaction types specified\n\n"); + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + + /* + * create the instance domain table ... one entry per transaction type + */ + if ((tx_indom = (pmdaInstid *)malloc(n * sizeof(pmdaInstid))) == NULL) { + fprintf(stderr, "malloc(%d): %s\n", + n * (int)sizeof(pmdaInstid), osstrerror()); + exit(1); + } + indomtab[0].it_numinst = n; + indomtab[0].it_set = tx_indom; + + /* + * size shm segment so each stat_t starts on a cache line boundary + */ + index_size = + RND_TO_CACHE_LINE(sizeof(control->level) + + sizeof(control->n_tx) + + n * sizeof(control->index[0])); + shm_size = index_size + n * RND_TO_CACHE_LINE(sizeof(stat_t)); + + /* + * create the shm segment, and install exit() handler to remove it + */ + if ((shmid = shmget(KEY, shm_size, IPC_CREAT|0666)) < 0) { + fprintf(stderr, "shmid: %s\n", osstrerror()); + exit(1); + } + atexit(done); + if ((control = (control_t *)shmat(shmid, NULL, 0)) == (control_t *)-1) { + fprintf(stderr, "shmat: %s\n", osstrerror()); + exit(1); + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "shmat -> control @ %p\n", control); + } +#endif + + /* + * set up the shm control info and directory + */ + control->n_tx = n; + control->level = 2; /* arbitrary default stats level */ + p = (char *)control; + p = &p[index_size]; + for (n = 0; n < control->n_tx; n++) { + /* + * Note: it is important that the index[] entries are byte + * offsets from the start of the shm segment ... using + * pointers may cause problems for 32-bit and 64-bit apps + * attaching to the shm segment + */ + control->index[n] = p - (char *)control; + sp = (stat_t *)p; + strncpy(sp->type, argv[opts.optind++], MAXNAMESIZE); + sp->type[MAXNAMESIZE-1] = '\0'; /* ensure null terminated */ + sp->reset_count = 0; + sp->count = 0; + sp->max_time = -1.0; + sp->sum_time = 0.0; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "index[%d]=%d @ %p name=\"%s\"\n", n, control->index[n], p, sp->type); + } +#endif + p += RND_TO_CACHE_LINE(sizeof(stat_t)); + + /* + * and set up the corresponding indom table entries + */ + tx_indom[n].i_inst = n; + tx_indom[n].i_name = sp->type; + } + + /* + * the real work is done below here ... + */ + txmon_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/txmon/txmon.h b/src/pmdas/txmon/txmon.h new file mode 100644 index 0000000..d7a74e8 --- /dev/null +++ b/src/pmdas/txmon/txmon.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * the txmon shm segment + * + * control comes at the beginning, with index[] expanded to match the + * number of transaction types (control->n_tx) + * + * then follows one stat_t per transaction type, subject to the + * constraint that each stat_t is hardware cache aligned to avoid + * anti-social bus traffic + */ + +typedef struct { + int level; /* controls stats collection levels */ + int n_tx; /* # of tx types */ + int index[1]; /* will be expanded when allocated */ +} control_t; + +static control_t *control; + +#define MAXNAMESIZE 20 + +typedef struct { + + /* managed by txmon PMDA ... do not fiddle with these! */ + char type[MAXNAMESIZE]; /* tx type name */ + unsigned int reset_count; /* tx count @ last reset */ + + /* initialized and then read by txmon PMDA, updated by txrecord */ + unsigned int count; /* tx count since epoch */ + float max_time; /* maximum elapsed time */ + double sum_time; /* aggregate elapsed time */ +} stat_t; + +/* arbitrary shm key */ +#define KEY (key_t)0xdeadbeef diff --git a/src/pmdas/txmon/txrecord.c b/src/pmdas/txmon/txrecord.c new file mode 100644 index 0000000..0289a9d --- /dev/null +++ b/src/pmdas/txmon/txrecord.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 1995-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include "txmon.h" + +/* + * Update the shm segment the is used to export metrics via the txmon PMDA + */ +int +main(int argc, char **argv) +{ + int shmid; + int n; + char *p; + stat_t *sp = NULL; /* initialize to pander to gcc */ + + if (argc == 1 || (argc == 2 && strcmp(argv[1], "-?") == 0)) { + fprintf(stderr, "Usage: txrecord tx_type servtime [tx_type servtime ...]\n" + " txrecord -l\n"); + exit(1); + } + + /* + * attach to the txmon PMDA shm segment ... + */ + if ((shmid = shmget(KEY, 0, 0)) < 0) { + fprintf(stderr, "Cannot attach to shm segment, shmid: %s\n", osstrerror()); + fprintf(stderr, "Is the txmon PMDA configured and running?\n"); + exit(1); + } + if ((control = (control_t *)shmat(shmid, NULL, 0)) == (control_t *)-1) { + fprintf(stderr, "Cannot attach to shm segment, shmat: %s\n", osstrerror()); + fprintf(stderr, "Is the txmon PMDA configured and running?\n"); + exit(1); + } + + if (control->level == 0) { + fprintf(stderr, "Stats collection disabled\n"); + exit(1); + } + else if (control->level == 1) + fprintf(stderr, "Warning: stats time collection disabled\n"); + + if (argc == 2 && strcmp(argv[1], "-l") == 0) { + printf("txmon shared memory segment summary\n"); + printf(" tx reset total maximum\n"); + printf("index offset count count time time name\n"); + for (n = 0; n < control->n_tx; n++) { + sp = (stat_t *)((__psint_t)control + control->index[n]); + printf("%5d %6d %6d %6d %9.3f %7.3f %s\n", + n, control->index[n], sp->count, sp->reset_count, + (float)sp->sum_time, sp->max_time, sp->type); + } + exit(0); + } + + while (argc > 1) { + + for (n = 0; n < control->n_tx; n++) { + sp = (stat_t *)((__psint_t)control + control->index[n]); + if (strcmp(argv[1], sp->type) == 0) + break; + } + argc--; + argv++; + if (argc == 1) { + fprintf(stderr, "Missing time for tx type \"%s\"?\n", argv[0]); + exit(1); + } + + if (n == control->n_tx) + fprintf(stderr, "Unknown tx type \"%s\" ... skipped\n", argv[0]); + + else { + double e; + e = strtod(argv[1], &p); + if (*p != '\0') + fprintf(stderr, "Time value (%s) for tx type \"%s\" is bogus ... skipped\n", argv[1], argv[0]); + else { + sp->count++; + if (control->level == 2) { + sp->sum_time += (float)e; + if ((float)e > sp->max_time) + sp->max_time = e; + } + } + } + argc--; + argv++; + } + + exit(0); +} diff --git a/src/pmdas/vmware/GNUmakefile b/src/pmdas/vmware/GNUmakefile new file mode 100644 index 0000000..d4cde6b --- /dev/null +++ b/src/pmdas/vmware/GNUmakefile @@ -0,0 +1,54 @@ +#!gmake +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = vmware +DOMAIN = VMWARE +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/vmware/Install b/src/pmdas/vmware/Install new file mode 100755 index 0000000..e834b40 --- /dev/null +++ b/src/pmdas/vmware/Install @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the VMware PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=vmware +perl_opt=true +daemon_opt=false +forced_restart=false + +perl -e "use VMware::VIRuntime" 2>/dev/null +if test $? -ne 0; then + echo "VMware Infrastructure Perl Toolkit is not installed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/vmware/Remove b/src/pmdas/vmware/Remove new file mode 100755 index 0000000..1291de4 --- /dev/null +++ b/src/pmdas/vmware/Remove @@ -0,0 +1,29 @@ +#! /bin/sh +# +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the VMware PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=vmware + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/vmware/pmdavmware.pl b/src/pmdas/vmware/pmdavmware.pl new file mode 100644 index 0000000..7e79924 --- /dev/null +++ b/src/pmdas/vmware/pmdavmware.pl @@ -0,0 +1,456 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2008 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; +use VMware::VIRuntime; + +use vars qw( $host $server $username $password ); +use vars qw( $pmda $entity $view $interval $metric_info %metric_values ); + +# Configuration files for setting VMware host connection parameters +for my $file ( pmda_config('PCP_PMDAS_DIR') . '/vmware/vmware.conf', + './vmware.conf' ) { + eval `cat $file` unless ! -f $file; +} +die 'VMware host not setup, stopped' unless defined($host); +die 'VMware server not setup, stopped' unless defined($server); +die 'VMware username not setup, stopped' unless defined($username); +die 'VMware password not setup, stopped' unless defined($password); + +Opts::set_option('server' => $server); +Opts::set_option('username' => $username); +Opts::set_option('password' => $password); +Opts::parse(); + +sub vmware_connect +{ + $Util::script_version = '1.0'; + Util::connect(); + $entity = Vim::find_entity_view(view_type => 'HostSystem', + filter => {'name' => $host}); + return unless defined($entity); + + $view = Vim::get_view(mo_ref => Vim::get_service_content()->perfManager); + my $summary = $view->QueryPerfProviderSummary(entity => $entity); + $interval = $summary->refreshRate; # absolute, in seconds + $interval = 20 unless defined($interval); # the vmware default +} + +sub vmware_timer_callback +{ + return unless !defined($entity); + $pmda->log("Connecting to VMware services on $host ($server)"); + vmware_connect(); + return unless defined($entity); + $pmda->log("Successfully connected."); +} + +sub vmware_disconnect +{ + Util::disconnect(); +} + +sub vmware_metric_ids +{ + my @filtered_list; + my $counter_info = $view->perfCounter; + my $available_id = $view->QueryAvailablePerfMetric(entity => $entity); + + foreach (@$counter_info) { + my $key = $_->key; + $metric_info->{$key} = $_; + } + + foreach (@$available_id) { + my $id = $_->counterId; + if (defined $metric_info->{$id}) { + my $metric = PerfMetricId->new(counterId => $id, instance => ''); + push @filtered_list, $metric; + } + } + return \@filtered_list; +} + +sub vmware_fetch +{ + return unless defined($entity); + + my @metric_ids = vmware_metric_ids(); + my $query_spec = PerfQuerySpec->new(entity => $entity, + metricId => @metric_ids, + 'format' => 'normal', + intervalId => $interval, + maxSample => 1); + my $values; + eval { + $values = $view->QueryPerf(querySpec => $query_spec); + }; + if ($@) { + if (ref($@) eq 'SoapFault') { + if (ref($@->detail) eq 'InvalidArgument') { + $pmda->log('QueryPerf parameters are not correct'); + } else { + $pmda->log('QueryPerf failed - Soap protocol error'); + } + } else { + $pmda->log('QueryPerf failed - cause unknown - good luck!'); + } + } + elsif (!@$values) { + $pmda->log('VMware performance data unavailable'); + } + + foreach (@$values) { + my $value_array = $_->value; + foreach (@$value_array) { + my $counter_id = $_->id->counterId; + my $counter = $metric_info->{$counter_id}; + my $key = $counter->nameInfo->label; + $metric_values{$key} = $_; + # $pmda->log("Value found: $key maps to $counter_id\n"); + } + } +} + +sub vmware_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + + if ($cluster == 0) { + if ($item == 0) { return ($host, 1); } + if ($item == 1) { return ($interval, 1); } + } + + return (PM_ERR_NOTCONN, 0) unless defined($entity); + + my $key = pmda_pmid_text($cluster, $item); + return (PM_ERR_PMID, 0) unless defined($key); + + # $pmda->log("vmware_fetch_callback $key $cluster:$item ($inst)\n"); + + my $pmvalue = $metric_values{$key}; + return (PM_ERR_AGAIN, 0) unless defined($pmvalue); + my $counters = ($pmvalue->value)[0]; + + if ($cluster == 0 && $item == 3) { + my $uptime = pmda_uptime($counters->[0]); + return ($uptime, 1); + } + return ($counters->[0], 1); +} + + +$pmda = PCP::PMDA->new('vmware', 90); + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.sys.host', + 'Name of monitored VMware host', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'vmware.sys.interval', + 'Interval at which VMware internally refreshes values', ''); +$pmda->add_metric(pmda_pmid(0,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'vmware.sys.uptime', 'Uptime', + 'Total time elapsed since last startup'); +$pmda->add_metric(pmda_pmid(0,3), PM_TYPE_STRING, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.sys.uptime_s', 'Uptime', + 'Total time elapsed since last startup'); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0), + 'vmware.net.usage', 'Network Usage (Average/Rate)', + 'Aggregated network performance statistics.'); + +$pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,-1,0,PM_SPACE_KBYTE,PM_TIME_SEC,0), + 'vmware.disk.usage', 'Disk Usage (Average/Rate)', + 'Aggregated storage performance statistics.'); + +$pmda->add_metric(pmda_pmid(3,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.mem.usage', 'Memory Usage (Average/Absolute)', + 'Memory usage as percentage of total configured or available memory'); +$pmda->add_metric(pmda_pmid(3,1), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), + 'vmware.mem.reserved_capacity', 'Memory Reserved Capacity', + 'Amount of memory reserved by the virtual machines'); +$pmda->add_metric(pmda_pmid(3,2), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.swap_in', 'Memory Swap In (Average/Absolute)', + 'Amount of memory that is swapped in'); +$pmda->add_metric(pmda_pmid(3,3), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.balloon', 'Memory Balloon (Average/Absolute)', + 'Amount of memory used by memory control'); +$pmda->add_metric(pmda_pmid(3,4), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.swap_out', 'Memory Swap Out (Average/Absolute)', + 'Amount of memory that is swapped out'); +$pmda->add_metric(pmda_pmid(3,5), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.unreserved', + 'Memory Unreserved (Average/Absolute)', + 'Amount of memory that is unreserved'); +$pmda->add_metric(pmda_pmid(3,6), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.heap', 'Memory Heap (Average/Absolute)', + 'Amount of memory allocated for heap'); +$pmda->add_metric(pmda_pmid(3,7), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.overhead', 'Memory Overhead (Average/Absolute)', + 'Amount of additional host memory allocated to the virtual machine'); +$pmda->add_metric(pmda_pmid(3,8), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.zeroed', 'Memory Zero (Average/Absolute)', + 'Amount of memory that is zeroed out'); +$pmda->add_metric(pmda_pmid(3,9), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.active', 'Memory Active (Average/Absolute)', + 'Amount of memory that is actively used'); +$pmda->add_metric(pmda_pmid(3,10), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.swap_used', 'Memory Swap Used (Average/Absolute)', + 'Amount of memory that is used by swap'); +$pmda->add_metric(pmda_pmid(3,11), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.shared', 'Memory Shared (Average/Absolute)', + 'Amount of memory that is shared'); +$pmda->add_metric(pmda_pmid(3,12), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.granted', 'Memory Granted (Average/Absolute)', + 'Amount of memory granted.'); +$pmda->add_metric(pmda_pmid(3,13), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.consumed', 'Memory Consumed (Average/Absolute)', + 'Amount of host memory consumed by the virtual machine for guest memory'); +$pmda->add_metric(pmda_pmid(3,14), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.heap_free', 'Memory Heap Free (Average/Absolute)', + 'Free space in memory heap'); +$pmda->add_metric(pmda_pmid(3,15), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.state', 'Memory State', + 'Memory State'); +$pmda->add_metric(pmda_pmid(3,16), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.shared_common', + 'Memory Shared Common (Average/Absolute)', + 'Amount of memory that is shared by common'); +$pmda->add_metric(pmda_pmid(3,17), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), + 'vmware.mem.vmkernel', 'Memory Used by vmkernel', + 'Amount of memory used by the vmkernel'); + +$pmda->add_metric(pmda_pmid(4,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE), + 'vmware.cpu.usage', 'CPU Usage (Average/Rate)', + 'CPU usage as a percentage over the collected interval'); +$pmda->add_metric(pmda_pmid(4,1), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE), + 'vmware.cpu.usage_mhz', 'CPU Usage in MHz (Average/Rate)', + 'CPU usage in MHz over the collected interval.'); +$pmda->add_metric(pmda_pmid(4,2), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,-1,1,0,PM_TIME_SEC,PM_COUNT_ONE), + 'vmware.cpu.reserved_capacity', 'CPU Reserved Capacity', + 'Total CPU capacity reserved by the virtual machines'); + +$pmda->add_metric(pmda_pmid(5,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.throttled.one_min_average', + 'CPU Throttled (1 min. average)', + 'Amount of CPU resources over the limit that were refused, average over 1 minute'); +$pmda->add_metric(pmda_pmid(5,1), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.throttled.five_min_average', + 'CPU Throttled (5 min. average)', + 'Amount of CPU resources over the limit that were refused, average over 5 minutes'); +$pmda->add_metric(pmda_pmid(5,2), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.throttled.fifteen_min_average', + 'CPU Throttled (15 min. average)', + "Amount of CPU resources over the limit that were refused,\n" + . "average over 15 minutes\n"); +$pmda->add_metric(pmda_pmid(5,3), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.one_min_peak', + 'CPU running peak over 1 minute', + ''); +$pmda->add_metric(pmda_pmid(5,4), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.five_min_peak', + 'CPU running peak over 5 minutes', + ''); +$pmda->add_metric(pmda_pmid(5,5), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.fifteen_min_peak', + 'CPU running peak over 15 minutes', + ''); +$pmda->add_metric(pmda_pmid(5,6), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,0,1,0,0,PM_COUNT_ONE), + 'vmware.rescpu.group_sample_count', + 'Group CPU Sample Count', 'Group CPU sample count'); +$pmda->add_metric(pmda_pmid(5,7), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.active.one_min_peak', + 'CPU Active (1 min. peak)', + 'CPU active peak over 1 minute'); +$pmda->add_metric(pmda_pmid(5,8), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.active.five_min_peak', + 'CPU Active (5 min. peak)', + 'CPU active peak over 5 minutes'); +$pmda->add_metric(pmda_pmid(5,9), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.active.fifteen_min_peak', + 'CPU Active (15 min. peak)', + 'CPU active peak over 15 minutes'); +$pmda->add_metric(pmda_pmid(5,10), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.one_min_average', + 'CPU Running (1 min. average)', + 'CPU running average over 1 minute'); +$pmda->add_metric(pmda_pmid(5,11), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.five_min_average', + 'CPU Running (5 min. average)', + 'CPU running average over 5 minutes'); +$pmda->add_metric(pmda_pmid(5,12), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.fifteen_min_average', + 'CPU Running (15 min. average)', + 'CPU running average over 15 minutes'); +$pmda->add_metric(pmda_pmid(5,13), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.one_min_peak', + 'CPU Running (1 min. peak)', + 'CPU running peak over 1 minute'); +$pmda->add_metric(pmda_pmid(5,14), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.five_min_peak', + 'CPU Running (5 min. peak)', + 'CPU running peak over 5 minutes'); +$pmda->add_metric(pmda_pmid(5,15), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.running.fifteen_min_peak', + 'CPU Running (15 min. peak)', + 'CPU running peak over 15 minutes'); +$pmda->add_metric(pmda_pmid(5,16), PM_TYPE_U64, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,1,0,0,PM_TIME_MSEC,0), + 'vmware.rescpu.group_sample_period', + 'Group CPU Sample Period', 'Group CPU sample period'); +$pmda->add_metric(pmda_pmid(5,17), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.active.one_min_average', + 'CPU Active (1 min. average)', + 'CPU active average over 1 minute'); +$pmda->add_metric(pmda_pmid(5,18), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.active.five_min_average', + 'CPU Active (5 min. average)', + 'CPU active average over 5 minutes'); +$pmda->add_metric(pmda_pmid(5,19), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'vmware.rescpu.active.fifteen_min_average', + 'CPU Active (15 min. average)', + 'CPU active average over 15 minutes'); + +$pmda->set_fetch(\&vmware_fetch); +$pmda->set_fetch_callback(\&vmware_fetch_callback); +$pmda->add_timer(5, \&vmware_timer_callback, 0); +$pmda->set_user('pcp'); +$pmda->run; +vmware_disconnect(); + +=pod + +=head1 NAME + +pmdavmware - VMware performance metrics domain agent (PMDA) + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which exports +metric values from a (possibly remote) VMware virtualisation host. + +This implementation uses the VMare Perl API (refer to the online +docs at http://www.vmware.com/support/developer/viperltoolkit). +VIPerl is a prerequisite for this PMDA, it needs to be installed +and configured before attempting to use this agent. It is highly +recommended that you test your VIPerl installation using the +demo programs that are shipped with VIPerl, before attempting to +use this PMDA. + +=head1 INSTALLATION + +In order to access performance data using the VIPerl API, it is +necessary to be able to login to the metrics source. Hence, a +valid VMware server name, user name and pass word are needed by +the PMDA. These can be passed in on the command line (via the +pmcd.conf file) or via a vmware.conf file in the PMDA directory. + + # cd $PCP_PMDAS_DIR/vmware + # [ edit vmware.conf ] + +This file should contain three lines, such as: + + $server = 'vm.server.net'; + $username = 'XXXX'; + $password = 'YYYY'; + +Once this is setup, you can access the names and values for the +vmware performance metrics by doing the following as root: + + # cd $PCP_PMDAS_DIR/vmware + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/vmware + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item $PCP_PMDAS_DIR/vmware/vmware.conf + +configuration file for the B agent + +=item $PCP_PMDAS_DIR/vmware/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/vmware/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/vmware.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/weblog/GNUmakefile b/src/pmdas/weblog/GNUmakefile new file mode 100644 index 0000000..aa68c72 --- /dev/null +++ b/src/pmdas/weblog/GNUmakefile @@ -0,0 +1,75 @@ +# +# Copyright (c) 2000-2001,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = weblog +DOMAIN = WEBSERVER +TARGETS = $(IAM)$(EXECSUFFIX) check_match$(EXECSUFFIX) +CFILES = weblog.c pmda.c sproc.c +HFILES = weblog.h +SCRIPTS = Install Remove server.sh weblogconv.sh +CHARTS = Web.Alarms.pmchart Web.Requests.pmchart Web.Volume.pmchart \ + Web.Allservers.pmchart Web.Perserver.Bytes.pmchart \ + Web.Perserver.Requests.pmchart +DFILES = README +LSRCFILES = pmns help $(DFILES) root $(SCRIPTS) check_match.c $(CHARTS) + +LDIRT = domain.h $(TARGETS) check_match.o + +PMDADIR = $(PCP_PMDAS_DIR)/weblog +PMCHARTDIR = $(PCP_VAR_DIR)/config/pmchart +CONFDIR = $(PCP_VAR_DIR)/config/web + +LDLIBS = $(PCP_PMDALIB) $(LIB_FOR_PTHREADS) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(TARGETS) + +install: build-me + # $(INSTALL) -d $(CONFDIR) + # $(INSTALL) -m 644 weblog.conf $(CONFDIR)/weblog.conf + $(INSTALL) -d $(PMDADIR) + $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM) + $(INSTALL) -m 755 check_match $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(DFILES) root help pmns domain.h $(PMDADIR) + $(INSTALL) -m 644 Web.Alarms.pmchart $(PMCHARTDIR)/Web.Alarms + $(INSTALL) -m 644 Web.Requests.pmchart $(PMCHARTDIR)/Web.Requests + $(INSTALL) -m 644 Web.Volume.pmchart $(PMCHARTDIR)/Web.Volume + $(INSTALL) -m 755 Web.Allservers.pmchart $(PMCHARTDIR)/Web.Allservers + $(INSTALL) -m 755 Web.Perserver.Bytes.pmchart $(PMCHARTDIR)/Web.Perserver.Bytes + $(INSTALL) -m 755 Web.Perserver.Requests.pmchart $(PMCHARTDIR)/Web.Perserver.Requests +else +build-me: +install: +endif + +weblog$(EXECSUFFIX): $(OBJECTS) + +weblog.o: domain.h + +check_match$(EXECSUFFIX): check_match.o + $(CCF) -o $@ $(LDFLAGS) check_match.o $(LDLIBS) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/weblog/Install b/src/pmdas/weblog/Install new file mode 100644 index 0000000..dcc86a1 --- /dev/null +++ b/src/pmdas/weblog/Install @@ -0,0 +1,694 @@ +#! /bin/sh +# +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the weblog PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# Override function from pmdaproc.sh +__choose_mode() +{ + if [ -n "$QUIET_INSTALL" ] ; then + do_pmda=true + else + __def=m + $do_pmda && __def=b + echo \ +'You will need to choose an appropriate configuration for installation of +the "'$iam'" Performance Metrics Domain Agent (PMDA). + + collector collect performance statistics on this system + monitor allow this system to monitor local and/or remote systems + both collector and monitor configuration for this system +' + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N 'Please enter c(ollector) or m(onitor) or b(oth) ['$__def'] '"$PCP_ECHO_C" + read ans + case "$ans" + in + "") break + ;; + c|collector|b|both) + do_pmda=true + break + ;; + m|monitor) + do_pmda=false + break + ;; + *) echo "Sorry, that is not acceptable response ..." + ;; + esac + done + echo + + fi +} + +iam=weblog +pmda_interface=2 +forced_restart=false + +pmdaSetup + +pmns_name=web # metric names differ from PMDA name +daemon_opt=true # can install as daemon +dso_opt=false +pipe_opt=true # pipe IPC - YES +socket_opt=false # socket IPC - NO +socket_inet_def=2080 # default TCP port for Internet socket IPC +check_delay=10 # give the PMDA a chance to set itself up + +# PMDA specific constants +# +configDir=$PCP_VAR_DIR/config/web + +# PMDA variables +# +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +debugFlag=0 +do_debug=false + +configFile="" +delay=15 +chkDelay=20 +maxserv=80 + + +# --- start functions --- +# +_parseDefaults() +{ + echo "Extracting options from current installation ..." + while getopts D:d:i:l:n:pS:t:u: c + do + case $c in + \?) echo "Warning: Unrecognized option in $PCP_PMCDCONF_PATH" + echo " Remove line for pmdaweblog in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2;; + D) debugFlag=$OPTARG;; + n) chkDelay=$OPTARG;; + t) delay=$OPTARG;; + S) maxserv=$OPTARG;; + *) # old or boring flags, silently ignore + ;; + esac + done + shift `expr $OPTIND - 1` + if [ $# -eq 1 ] + then + configFile=$1 + elif [ $# -eq 0 ] + then + configFile="" + else + echo "Warning: unrecognized format for old specification in $PCP_PMCDCONF_PATH" + echo " Remove line for pmdaweblog in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2 + fi +} + +_defaultRegex() +{ + touch $1 + echo ' +# Common regular expressions specifications for parsing access and error logs +# Each regular expression specification should have a name (one word), +# specify the order of regex parameters (method and size), and +# a regular expression. Regular expressions for access logs require two +# arguments to be set while errors logs require only a match. +# +# Set the online HTML Users and Administrators Guide, pmdaweblog(1) and +# regexec(3) for more details. +# + +# pattern for CERN, NCSA, Netscape, Apache etc Access Logs +regex_posix CERN method,size ][ \\]+"([A-Za-z][-A-Za-z]+) [^"]*" [-0-9]+ ([-0-9]+) +# pattern for CERN, NCSA, Netscape etc Error Logs +regex_posix CERN_err - . +# pattern for Proxy Server Extended Log Format +regex_posix NS_PROXY 1,3,2,4 ][ ]+"([A-Za-z][-A-Za-z]+) [^"]*" ([-0-9]+) ([-0-9]+) ([-0-9]+) +# pattern for Squid Cache logs +regex_posix SQUID 4,3,2,1 [0-9]+\.[0-9]+[ ]+[0-9]+ [a-zA-Z0-9\.]+ ([_A-Z]+)\/([0-9]+) ([0-9]+) ([A-Z]+) +# pattern for Netscape SOCKS Server Access logs +regex_posix NS_SOCKS method,size (sockd)\[.*, ([0-9]+) bytes from .* \(http\) +# pattern for Netscape SOCKS Server Error logs +regex_posix NS_SOCKS_err - . +# pattern for FTP through a Netscape SOCKS Server Access log +regex_posix NS_FTP method,size (sockd)\[.*, ([0-9]+) bytes from .* \([0-9]+\) +# pattern for FTP through a Netscape SOCKS Server Error logs +regex_posix NS_FTP_err - . +# pattern for FTP Server access logs (normally in SYSLOG) +regex_posix SYSLOG_FTP method,size ftpd\[.*\]: ([gp][-A-Za-z]+)( ) +# pattern for FTP Server error logs (normally in SYSLOG) +regex_posix SYSLOG_FTP_err - FTP LOGIN FAILED +# pattern for WU_FTP Server access logs (normally in xferlog) +regex_posix WU_FTP size,method :[0-9][0-9] [0-9]+ [0-9]+ .+ ([0-9]+) .+ [ba] .+ ([io]) [arg] +# pattern for WU_FTP Server error logs (normally in SYSLOG/messages) +regex_posix WU_FTP_err - failed login + +# Server specifications. The format of each specification is +# "server" serverName on|off accessRegex accessFile errorRegex errorFile +# +# Set the online HTML Users and Administrators Guide and pmdaweblog(1) +# for more details. +#' >> $1 +} + +_parse_server() +{ + egrep "^server" | $PCP_AWK_PROG ' + { i=index($2, ":"); + if (i == 0) { + name = $2; + port = ""; + } + else { + name = substr($2,1,i-1); + port = sprintf("Port %d", substr($2, i+1, length($2) - i)); + } + printf("Server %s %s\n", name, port); + printf(" Access Log: %s (%s)\n", $5, $4); + printf(" Error Log: %s (%s)\n\n", $7, $6); + }' +} + +_default_config () +{ + rm -f $tmp/conf + touch $tmp/conf + _defaultRegex $tmp/conf + ./server.sh -q -l $tmp/conf + egrep "^server" $tmp/conf > /dev/null 2>&1 + _st=$? + if [ $_st -eq 0 ] + then + ./pmdaweblog -C $tmp/conf >$tmp/out 2>&1 + _st=$? + if [ $_st -eq 0 ] ; then + if [ -z "$configFile" ] + then + configFile=$configDir/$iam.conf + fi + rm -f $configFile + cp $tmp/conf $configFile + args="-D $debugFlag -t $delay -n $chkDelay -S $maxserv $configFile" + socket_opt=false + fi + fi + return $_st +} + +# +# --- end functions --- + +if $do_pmda +then + + [ ! -d $configDir ] && mkdir -p $configDir + + if [ -n "$QUIET_INSTALL" ] ; then + _default_config + if [ $? -eq 0 ] ; then + pmdaInstall + exit $? + else + exit 1 + fi + else + echo "----------------------------------------------------------------" + echo + echo "The default installation of the weblog PMDA will search for known" + echo "Web server configurations on this host and will setup the weblog" + echo "PMDA to monitor all associated Web server log files." + echo + echo "Otherwise, you will be prompted for the required information." + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a default weblog PMDA installation [y] ""$PCP_ECHO_C" + read ans + echo + if [ "X$ans" = X -o "X$ans" = Xy -o "X$ans" = XY ] + then + _default_config + if [ $? -eq 0 ] ; then + pmdaInstall + exit $? + else + echo + echo "Unable to find any Web servers!" + echo "Reverting to detailed installation..." + fi + fi + + echo "----------------------------------------------------------------" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Checking for a previous PMDA installation ...""$PCP_ECHO_C" + + # weblogs -> weblog can be removed once all 1.0 betas are known to + # have gone away + ans=`$PCP_AWK_PROG < $PCP_PMCDCONF_PATH ' + $1 == "'$iam'" { + printf "%s",$6 + for (i=7;i<=NF;i++) printf " %s",$i + print "" + }'` + if [ -n "$ans" ] + then + echo " found" + _parseDefaults $ans + else + echo " appears to be a first-time install" + fi + + if [ -n "$configFile" ] + then + if [ -f "$configFile" ] + then + if [ $PCP_PLATFORM = linux ] && \ + egrep '^regex ' $configFile > /dev/null + then + echo "Warning: previous configuration file \"$configFile\"" + echo " appears to be an incompatible version." + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to automatically update the configuration file? [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X -o "$ans" = "y" -o "$ans" = "Y" ] + then + ./weblogconv.sh $configFile $tmp/conf + if ./pmdaweblog -C $tmp/conf > /dev/null 2>&1 + then + cp $tmp/conf $configFile + else + echo "Warning: automatic conversion failed." + echo "You can either continue, and use the default configuration file or exit" + echo "this install procedure to manually update your existing configuration." + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to continue with the default configuration? [n] ""$PCP_ECHO_C" + read ans + if [ "$ans" = "y" -o "$ans" = "Y" ] + then + configFile="" + else + exit 1 + fi + fi + fi + else + echo "Using previous configuration file \"$configFile\"" + fi + else + echo "Warning: previous configuration file \"$configFile\" no longer" + echo " exists, reverting to default" + configFile="" + fi + fi + + if [ "X$configFile" = X -a -f $configDir/$iam.conf ] + then + configFile=$configDir/$iam.conf + echo "Using previous configuration file \"$configFile\"" + fi + + if [ "X$configFile" != X ] + then + if [ -f $configFile ] + then + echo "The inital configuration file contains the following Web server details:" + echo + cat $configFile | _parse_server | ${PAGER-more} + echo + echo "------------------------------------------------------------------------------" + + if ./pmdaweblog -C $configFile >$tmp/out 2>&1 + then + : + else + echo "Warning: parsing this configuration file produced the following errors," + echo " and this file will be ignored." + + cat $tmp/out + echo + if [ "X$configFile" = "X$tmp/default" ] + then + echo "Arrgh ... this is the default configuration, I cannot recover from here!" + exit 1 + fi + configFile="" + fi + fi + fi + + echo + echo "A configuration file can be automatically generated. This can" + echo "be used to compare or replace an existing configuration file." + echo + + if [ "X$configFile" = X ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a configuration file to be automatically generated [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X ] + then + ans="y" + fi + else + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a configuration file to be automatically generated [n] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X ] + then + ans="n" + fi + fi + + if [ "X$ans" = "Xy" -o "X$ans" = "XY" ] + then + echo + echo "Now scanning for Web servers ..." + echo + + if [ ! -x ./server.sh ] + then + echo "Unable to scan for Web servers as ./server.sh is missing!" + else + rm -f $tmp/conf + touch $tmp/conf + _defaultRegex $tmp/conf + ./server.sh -l $tmp/conf + if egrep "^server" $tmp/conf > /dev/null 2>&1 + then + echo + echo "This is a possible configuration file for your system:" + echo + cat $tmp/conf | _parse_server | ${PAGER-more} + echo + echo "------------------------------------------------------------------------------" + echo + + if ./pmdaweblog -C $tmp/conf > /dev/null 2>&1 + then + if [ "X$configFile" = X ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like to use this configuration file [y] ""$PCP_ECHO_C" + read ans + if [ "X$ans" = "Xy" -o "X$ans" = "XY" -o "X$ans" = X ] + then + cp $tmp/conf $configDir/$iam.conf + configFile=$configDir/$iam.conf + fi + else + echo "Would you like to replace your existing configuration file with" + $PCP_ECHO_PROG $PCP_ECHO_N "the generated file [n] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != "Xn" -a "X$and" != "XN" -a "X$ans" != X ] + then + cp $tmp/conf $configFile + fi + fi + else + echo "Automated configuration file generation is broken!" + if [ "X$configFile" = X ] + then + echo "Please consult the manual on how to create a configuration file." + echo "Installation failed." + exit 1 + else + echo "Ignoring this file." + fi + fi + else + echo + echo "I could not find any Web servers." + fi + echo + fi + fi + + echo "------------------------------------------------------------------------------" + + echo + if [ "X$configFile" = X ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want to specify some Web servers [n]: ""$PCP_ECHO_C" + serverAdded="false" + else + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want to specify some more Web servers [n]: ""$PCP_ECHO_C" + serverAdded="true" + fi + + read ans + while [ "X$ans" = "Xy" -o "X$ans" = "XY" ] + do + if [ "X$configFile" = X ] + then + if [ "X$configFile" = X -a -f $configDir/$iam.conf ] + then + echo "Replacing existing configuration file $configDir/$iam.conf" + rm -f $configDir/$iam.conf + else + echo "Creating configuration file $configDir/$iam.conf" + fi + _defaultRegex $configDir/$iam.conf + configFile="$configDir/$iam.conf" + fi + + echo + serverName=`hostname` + $PCP_ECHO_PROG $PCP_ECHO_N "The name of the Web server [$serverName]: ""$PCP_ECHO_C" + read ans + if [ "X$ans" = X ] + then + serverName=`hostname` + else + serverName=$ans + fi + + echo + accessPath="" + while [ "X$accessPath" = X ] + do + $PCP_ECHO_PROG $PCP_ECHO_N "The path to the access log: + ""$PCP_ECHO_C" + read accessPath + if [ "X$accessPath" != X ] + then + if [ -f $accessPath ] + then + : + else + echo "$accessPath does not exist or is not a regular file" + accessPath="" + fi + fi + done + + echo + errorPath="" + while [ "X$errorPath" = X ] + do + $PCP_ECHO_PROG $PCP_ECHO_N "The path to the error log: + ""$PCP_ECHO_C" + read errorPath + if [ "X$errorPath" != X ] + then + if [ -f $errorPath ] + then + : + else + echo "$errorPath does not exist or is not a regular file" + errorPath="" + fi + fi + done + + echo + echo "The configuration file contains these specifications:" + echo + ${PAGER-more} $configFile + echo + echo "Does the configuration file contain appropriate regular expressions" + $PCP_ECHO_PROG $PCP_ECHO_N "for the \"$serverName\" Web server [y]: ""$PCP_ECHO_C" + read ans + echo + if [ "X$ans" = "Xn" -o "X$ans" = "XN" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to quit the installation to add new regular expressions [y]: ""$PCP_ECHO_C" + read ans + if [ "$Xans" = "Xy" -o "X$ans" = "XY" -o "X$ans" = X ] + then + echo "Edit $configFile and then rerun this Install script." + exit 1 + echo + echo "Skipping $serverName ..." + fi + else + accessRegex="" + while [ "X$accessRegex" = X ] + do + if egrep "^regex_posix CERN " $configFile > /dev/null 2>&1 + then + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the access log [CERN]: ""$PCP_ECHO_C" + accessRegex="CERN" + else + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the access log: ""$PCP_ECHO_C" + accessRegex="" + fi + read ans + if [ "X$ans" != X ] + then + accessRegex=$ans + fi + if [ "X$accessRegex" != X ] + then + if egrep "^regex_posix $accessRegex " $configFile > /dev/null 2>&1 + then + : + else + echo "Could not find $accessRegex in $configFile" + accessRegex="" + fi + fi + done + + echo + errorRegex="" + while [ "X$errorRegex" = X ] + do + if egrep "^regex_posix CERN_err " $configFile > /dev/null 2>&1 + then + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the error log [CERN_err]: ""$PCP_ECHO_C" + errorRegex="CERN_err" + else + $PCP_ECHO_PROG $PCP_ECHO_N "The regex for the error log: ""$PCP_ECHO_C" + errorRegex="" + fi + read ans + if [ "X$ans" != X ] + then + errorRegex=$ans + fi + if [ "X$errorRegex" != X ] + then + if egrep "^regex_posix $errorRegex " $configFile > /dev/null 2>&1 + then + : + else + echo "Could not find $errorRegex in $configFile" + errorRegex="" + fi + fi + done + + echo + echo "You have specified the following Web server:" + echo + server="server $serverName on $accessRegex $accessPath $errorRegex $errorPath" + echo "$server" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Is this correct [y]: + ""$PCP_ECHO_C" + read ans + if [ "X$ans" = "Xy" -o "X$ans" = "XY" -o "X$ans" = X ] + then + echo >> $configFile + echo "# User configured server called \"$serverName\"" >> $configFile + echo $server >> $configFile + serverAdded="true" + fi + fi + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to specify another Web Server [n]: ""$PCP_ECHO_C" + read ans + echo + done + + if [ "$serverAdded" = "false" ] + then + rm -f $configFile + configFile="" + fi + + if [ "X$configFile" = X ] + then + echo "Please consult the manual on how to create a configuration file." + echo "Installation failed as no servers were specified." + exit 1 + fi + + echo + echo "You may modify the configuration file by hand and add servers" + echo "that are not currently listed, change their names, etc." + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Do you wish to exit and modify the configuration file [n] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != "Xn" -a "X$ans" != "XN" -a "X$ans" != X ] + then + echo + echo "Edit $configFile and then rerun this Install script." + exit 1 + fi + + echo + echo "------------------------------------------------------------------------------" + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "The delay in seconds between forced reads of the log files [$delay] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + delay=$ans + fi + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Number of seconds of inactivity before checking for log rotation [$chkDelay] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + chkDelay=$ans + fi + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "The maximum number of servers per agent process [$maxserv] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + maxserv=$ans + fi + + if [ "$do_debug" = true ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "the Debugging Flag (see pmdbg(1)) [$debugFlag] ""$PCP_ECHO_C" + read ans + if [ "X$ans" != X ] + then + debugFlag=$ans + fi + fi + + args="-D $debugFlag -t $delay -n $chkDelay -S $maxserv $configFile" + + echo + echo "------------------------------------------------------------------------------" + echo + fi +fi + +pmdaInstall + +exit 0 + diff --git a/src/pmdas/weblog/README b/src/pmdas/weblog/README new file mode 100644 index 0000000..611a188 --- /dev/null +++ b/src/pmdas/weblog/README @@ -0,0 +1,205 @@ +Performance Co-Pilot Weblog PMDA for Monitoring of Web Server logs +================================================================== + +This PMDA is capable of monitoring the activity of multiple Web servers, +in terms of requests and bytes, in real time. The PMDA can also monitor +proxy server, SOCKS server and ftpd logs. + +Site configuration is discussed in the online HTML documentation located +at $PCP_DOC_DIR/pcpweb. This should be read before proceeding any further +with installing this PMDA. The file $PCP_DOC_DIR/pcpweb/README contains +instructions for installing this documentation. + +During the installation process, you may be prompted for several +parameters which will affect the behavior of the weblog PMDA. These +are discussed in the pmdaweblog(1) man page. + + +Installation of the Weblog PMDA +=============================== + +1. Check that there is no clash with the Performance Metrics Domain + number defined in domain.h and the other PMDAs currently in use + (see $PCP_PMCDCONF_PATH). If there is, edit domain.h and choose + another domain number. + +2. Ensure that the web server control files can be correctly located as + follows. + + Web Server Default Directory Environment Search for Config + Type Variable File(s) and/or Logs + Below the Default + Directory + + Netscape /usr/ns-home $NSROOTPATH httpd-*/obj.conf + and httpd-*/magnus.conf + /var/netscape/suitespot https-*/obj.conf + https-*/magnus.conf + proxy-*/obj.conf + proxy-*/magnus.conf + + Netscape /usr/ns-home $NSROOTPATH proxy-server/logs/sockd + Proxy + + Netscape /var/ns-proxy $NSPROXYPATH logs/access + Proxy logs/errors + logs/sockd + + Outbox /var/www/htdocs/outbox $OUTBOXPATH logs/access + logs/errors + + NCSA /var/www $NCSAPATH server/logs/access_log + server/logs/error_log + + Zeus /usr/local/zeus $ZEUSPATH server.ini + log/transfer + log/errors + + Apache /usr/apache $APACHEPATH conf/httpd.conf + conf/srm.conf + log/access_log + log/error_log + + Anon FTP /etc/passwd $PASSWDPATH [file, not dir] for ~ftp + /var/adm/SYSLOG $SYSLOGPATH [file, not dir] for + access and errors + + To over-ride the Default Directory for a particular type of Web + server, set the corresponding Environment Variable to the absolute + pathname of the directory. As a special case $NSROOTPATH for the + non-proxy Netscape Web server can be set to a colon (:) separated + list of directory names to be searched (in the style of the $PATH + for /bin/sh). + + +3. Then run the Install script (as root) + + # cd $PCP_PMDAS_DIR/weblog + # ./Install + +4. The installation script will prompt if this is a collector and/or + monitor installation. Briefly: + + o if there are Web servers on this host, then this is a collector host. + + o if monitoring tools (pmchart(1), pmlogger(1) etc.) will be run on + this host, then this is a monitoring host. + + Consult the HTML documentation for more details. A monitoring host + installation will install only the namespace and some application + configuration files. + +5. The next prompt will ask if this is a default installation. The + default installation will search for known Web server configurations + and install the PMDA to monitor any logs that are found. This is + appropriate for first time installations. The non-default + installation is described in points 6 to 8. + +6. The configuration file for the weblog PMDA must be found and + checked. The Install script will look in the likely places for an + existing file and prompt for confirmation. Otherwise, a + configuration file can be automatically generated by searching known + Web server configuration files and directories. + +7. The second stage of the Install script prompts for the pmdaweblog(1) + parameters. The default values should be adequate for an initial + installation. + +8. The final stage will install the agent and restart PMCD (the + Performance Metrics Collection Daemon). The Install script should + report that the Metrics are OK. + + +De-installation +=============== + +Simply use (as root) + + # cd $PCP_PMDAS_DIR/weblog + # ./Remove + + +Changing the settings +===================== + +The safest way to alter any settings that were entered in the Install +script is to re-run the Install script. Changes to the weblog.conf file +can be also be registered by running the Install script. + +To quickly test changes to the configuration files, the agent and pmcd +can be restarted as follows: + + To register any changes made to the weblog.conf file, the agent + must be killed and restarted: + + # pmsignal -a -s KILL pmdaweblog + # pmsignal -a -s HUP pmcd + + To register any changes to the $PCP_PMCDCONF_PATH file you must + restart PMCD: + + # $PCP_RC_DIR/pcp start + + +Troubleshooting +=============== + +0. If there is trouble locating the Web server access and error logs, + try running the server.sh script with diagnostics: + + $ cd $PCP_PMDAS_DIR/weblog + $ ./server.sh -q -v $tmp/sed + +pmprobe -I $* web.perserver.bytes.cached.total web.perserver.bytes.total > $tmp/pmprobe +l1=`head -n 1 $tmp/pmprobe` +l2=`tail -n 1 $tmp/pmprobe` + +num_caches=`echo $l1 | cut -f2 -d\ ` +num_servers=`echo $l2 | cut -f2 -d\ ` +if [ $num_servers -gt 0 ] +then + caches=`echo $l1 | cut -f3- -d\ ` + servers=`echo $l2 | cut -f3- -d\ ` +# hostname=`echo $servers | cut -f1 -d: | sed -f $tmp/sed` + + if [ $num_caches -le 0 ] + then + # an old pmda - quietly handle all servers as if they were CERN - show only totals + caches="NeVeR_MaTcH" + num_caches=0 + fi +elif [ $num_servers -eq 0 ] +then + $PCP_XCONFIRM_PROG -c -B OK -header "No Active Servers - cannot continue" \ + -t "$message" \ + -icon info > /dev/null + exit +else + message=`pmerr $num_servers | cut -f5- -d\ ` + $PCP_XCONFIRM_PROG -c -B OK -header "Fatal error - cannot continue" \ + -t "$message" \ + -icon error > /dev/null + exit +fi + +# +# if too many instances, turn off all legends +# +legendp=on +if [ $num_servers -gt 6 ] +then + legendp=off +fi + +if [ $num_servers -gt 12 ] +then + $PCP_XCONFIRM_PROG -c -B Cancel -b Continue -header \ + "Too many charts" \ + -t "There is 1 chart per server, more than can reasonably be displayed on the screen" \ + -icon warning | grep Cancel >/dev/null +if [ $? -eq 0 ] +then + exit +fi +fi + +# chart preamble +# +cat > $tmp/base <> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.requests.total >> $tmp/base + echo Chart Title \"Total Bytes sent by all servers \" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.bytes.total >> $tmp/base +fi +if [ $num_caches -gt 0 ] +then + echo Chart Title \"Total Requests serviced by caching servers \" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#FFFF30 Host \* Metric web.allservers.requests.client.total >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.allservers.requests.cached.total >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.requests.uncached.total >> $tmp/base + echo Chart Title \"Total Bytes sent by caching servers \" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.allservers.bytes.cached.total >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.allservers.bytes.uncached.total >> $tmp/base +fi + +cat $tmp/base +rm -rf $tmp diff --git a/src/pmdas/weblog/Web.Perserver.Bytes.pmchart b/src/pmdas/weblog/Web.Perserver.Bytes.pmchart new file mode 100755 index 0000000..1f57a27 --- /dev/null +++ b/src/pmdas/weblog/Web.Perserver.Bytes.pmchart @@ -0,0 +1,90 @@ +#!/bin/sh + +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +echo "/\"/s///g" >$tmp/sed + +pmprobe -I $* web.perserver.bytes.cached.total web.perserver.bytes.total > $tmp/pmprobe +l1=`head -n 1 $tmp/pmprobe` +l2=`tail -n 1 $tmp/pmprobe` + +num_caches=`echo $l1 | cut -f2 -d\ ` +num_servers=`echo $l2 | cut -f2 -d\ ` +if [ $num_servers -gt 0 ] +then + caches=`echo $l1 | cut -f3- -d\ ` + servers=`echo $l2 | cut -f3- -d\ ` + + if [ $num_caches -lt 0 ] + then + # an old pmda - quietly handle all servers as if they were CERN - show only totals + caches="NeVeR_MaTcH" + num_caches=0 + fi +elif [ $num_servers -eq 0 ] +then + $PCP_XCONFIRM_PROG -c -B OK -header "No Active Servers - cannot continue" \ + -t "$message" \ + -icon info > /dev/null + exit +else + message=`pmerr $num_servers | cut -f5- -d\ ` + $PCP_XCONFIRM_PROG -c -B OK -header "Fatal error - cannot continue" \ + -t "$message" \ + -icon error > /dev/null + exit +fi + +# +# if too many instances, turn off all legends +# +legendp=on +if [ $num_servers -gt 6 ] +then + legendp=off +fi + +if [ $num_servers -gt 12 ] +then + $PCP_XCONFIRM_PROG -c -B Cancel -b Continue -header \ + "Too many charts" \ + -t "There is 1 chart per server, more than can reasonably be displayed on the screen" \ + -icon warning | grep Cancel >/dev/null +if [ $? -eq 0 ] +then + exit +fi +fi + +# chart preamble +# +cat > $tmp/base </dev/null +if [ $? -eq 0 ] +then + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Bytes sent by $j\" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.perserver.bytes.cached.total Instance $j >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.bytes.uncached.total Instance $j >> $tmp/base +else + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Total Bytes sent by $j\" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.bytes.total Instance $j >> $tmp/base +fi +i=`expr $i + 1` +done + +cat $tmp/base +rm -rf $tmp diff --git a/src/pmdas/weblog/Web.Perserver.Requests.pmchart b/src/pmdas/weblog/Web.Perserver.Requests.pmchart new file mode 100755 index 0000000..77ff366 --- /dev/null +++ b/src/pmdas/weblog/Web.Perserver.Requests.pmchart @@ -0,0 +1,91 @@ +#!/bin/sh + +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +echo "/\"/s///g" >$tmp/sed + +pmprobe -I $* web.perserver.bytes.cached.total web.perserver.bytes.total > $tmp/pmprobe +l1=`head -n 1 $tmp/pmprobe` +l2=`tail -n 1 $tmp/pmprobe` + +num_caches=`echo $l1 | cut -f2 -d\ ` +num_servers=`echo $l2 | cut -f2 -d\ ` +if [ $num_servers -gt 0 ] +then + caches=`echo $l1 | cut -f3- -d\ ` + servers=`echo $l2 | cut -f3- -d\ ` + + if [ $num_caches -lt 0 ] + then + # an old pmda - quietly handle all servers as if they were CERN - show only totals + caches="NeVeR_MaTcH" + num_caches=0 + fi +elif [ $num_servers -eq 0 ] +then + $PCP_XCONFIRM_PROG -c -B OK -header "No Active Servers - cannot continue" \ + -t "$message" \ + -icon info > /dev/null + exit +else + message=`pmerr $num_servers | cut -f5- -d\ ` + $PCP_XCONFIRM_PROG -c -B OK -header "Fatal error - cannot continue" \ + -t "$message" \ + -icon error > /dev/null + exit +fi + +# +# if too many instances, turn off all legends +# +legendp=on +if [ $num_servers -gt 6 ] +then + legendp=off +fi + +if [ $num_servers -gt 12 ] +then + $PCP_XCONFIRM_PROG -c -B Cancel -b Continue -header \ + "Too many charts" \ + -t "There is 1 chart per server, more than can reasonably be displayed on the screen" \ + -icon warning | grep Cancel >/dev/null +if [ $? -eq 0 ] +then + exit +fi +fi + +# chart preamble +# +cat > $tmp/base </dev/null +if [ $? -eq 0 ] +then + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Requests satisfied by $j\" Style stacking Legend $legendp >> $tmp/base + echo Plot Color \#FFFF30 Host \* Metric web.perserver.requests.client.total Instance $j >> $tmp/base + echo Plot Color \#3030FF Host \* Metric web.perserver.requests.cached.total Instance $j >> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.requests.uncached.total Instance $j >> $tmp/base +else + j=`echo $server | sed -f $tmp/sed` + echo Chart Title \"Total Requests satisfied by $j\" Style bars Legend off>> $tmp/base + echo Plot Color \#FF3030 Host \* Metric web.perserver.requests.total Instance $j >> $tmp/base +fi +i=`expr $i + 1` +done + +cat $tmp/base +rm -rf $tmp diff --git a/src/pmdas/weblog/Web.Requests.pmchart b/src/pmdas/weblog/Web.Requests.pmchart new file mode 100644 index 0000000..7775fd5 --- /dev/null +++ b/src/pmdas/weblog/Web.Requests.pmchart @@ -0,0 +1,27 @@ +#pmchart +# +# Web statistics (request rates) +# +# This file is installed by the script $PCP_PMDAS_DIR/weblog/Install +# +Version 2.0 host dynamic + +Chart Title "Requests by HTTP method" Style stacking + Plot Color rgbi:1.0/1.0/0.0 Host * Metric web.allservers.requests.get + Plot Color rgbi:0.0/1.0/1.0 Host * Metric web.allservers.requests.post + Plot Color rgbi:1.0/0.0/1.0 Host * Metric web.allservers.requests.head + Plot Color rgbi:1.0/1.0/0.6 Host * Metric web.allservers.requests.other +Chart Title "Requests by request size" Style stacking + Plot Color rgbi:1.0/0.8/0.6 Host * Metric web.allservers.requests.size.zero + Plot Color rgbi:0.6/1.0/0.6 Host * Metric web.allservers.requests.size.le3k + Plot Color rgbi:0.8/0.6/1.0 Host * Metric web.allservers.requests.size.le10k + Plot Color rgbi:1.0/0.65/0.3 Host * Metric web.allservers.requests.size.le30k + Plot Color rgbi:0.3/1.0/0.3 Host * Metric web.allservers.requests.size.le100k + Plot Color rgbi:0.65/0.3/1.0 Host * Metric web.allservers.requests.size.le300k + Plot Color rgbi:1.0/0.5/0.0 Host * Metric web.allservers.requests.size.le1m + Plot Color rgbi:0.0/1.0/0.0 Host * Metric web.allservers.requests.size.le3m + Plot Color rgbi:0.6/0.0/0.9 Host * Metric web.allservers.requests.size.gt3m + Plot Color rgbi:1.0/0.35/0.0 Host * Metric web.allservers.requests.size.unknown + +# +# Created Thu Jul 2 10:48:19 1998 diff --git a/src/pmdas/weblog/Web.Volume.pmchart b/src/pmdas/weblog/Web.Volume.pmchart new file mode 100644 index 0000000..569abd9 --- /dev/null +++ b/src/pmdas/weblog/Web.Volume.pmchart @@ -0,0 +1,25 @@ +#pmchart +# +# Web Statistics (data volume) +# +# This file is installed by the script $PCP_PMDAS_DIR/weblog/Install +# +Version 2.0 host dynamic + +Chart Title "Bytes sent by HTTP method" Style stacking + Plot Color rgbi:1.0/1.0/0.0 Host * Metric web.allservers.bytes.get + Plot Color rgbi:0.0/1.0/1.0 Host * Metric web.allservers.bytes.post + Plot Color rgbi:1.0/0.0/1.0 Host * Metric web.allservers.bytes.head + Plot Color rgbi:1.0/1.0/0.6 Host * Metric web.allservers.bytes.other +Chart Title "Bytes sent by request size" Style stacking + Plot Color rgbi:0.6/1.0/0.6 Host * Metric web.allservers.bytes.size.le3k + Plot Color rgbi:0.8/0.6/1.0 Host * Metric web.allservers.bytes.size.le10k + Plot Color rgbi:1.0/0.65/0.3 Host * Metric web.allservers.bytes.size.le30k + Plot Color rgbi:0.3/1.0/0.3 Host * Metric web.allservers.bytes.size.le100k + Plot Color rgbi:0.65/0.3/1.0 Host * Metric web.allservers.bytes.size.le300k + Plot Color rgbi:1.0/0.5/0.0 Host * Metric web.allservers.bytes.size.le1m + Plot Color rgbi:0.0/1.0/0.0 Host * Metric web.allservers.bytes.size.le3m + Plot Color rgbi:0.6/0.0/0.9 Host * Metric web.allservers.bytes.size.gt3m + +# +# Created Thu Jul 2 10:47:51 1998 diff --git a/src/pmdas/weblog/check_match.c b/src/pmdas/weblog/check_match.c new file mode 100644 index 0000000..5166e75 --- /dev/null +++ b/src/pmdas/weblog/check_match.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2000,2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Uses the same regular expression logic as pmdaweblog, but extracted + * here so new patterns and access logs can be tested + * + * Usage: check_match configfile pat_name [input] + * configfile regex spec file as used by pmdaweblog + * pat_name use only this names regex from configfile + * input test input to try and match, defaults to stdin + */ + +#include +#include +#if defined(HAVE_REGEX_H) +#include +#endif +#include + +int +main(int argc, char *argv[]) +{ + FILE *fc; + char buf[1024]; + char *p; + char *q; +#ifdef HAVE_REGEX + char *comp = NULL; + char sub0[1024]; + char sub1[1024]; + char sub2[1024]; + char sub3[1024]; +#endif + int lno = 0; + int regex_posix = 0; + int cern_format = 0; + int common_extended_format = 0; + int squid_format = 0; + int methodpos = 1, c_statuspos = 2, sizepos = 2, s_statuspos = 2; + long client_cache_hits, proxy_cache_hits, remote_fetches; + double proxy_bytes, remote_bytes; +#if (defined HAVE_REGEXEC) && (defined HAVE_REGCOMP) + regex_t re = {0}; + regmatch_t pmatch[5]; + size_t nmatch = 5; +#endif + + + if (argc < 3 || argc > 4) { + fprintf(stderr, "Usage: check_match configfile pat_name [input]\n"); + exit(1); + } + + if ((fc = fopen(argv[1], "r")) == NULL) { + fprintf(stderr, "check_match: cannot open configfile \"%s\": %s\n", argv[1], osstrerror()); + exit(1); + } + + if (argc == 4) { + if (freopen(argv[3], "r", stdin) == NULL) { + fprintf(stderr, "check_match: cannot open input \"%s\": %s\n", argv[3], osstrerror()); + exit(1); + } + } + + while (fgets(buf, sizeof(buf), fc) != NULL) { + lno++; + + if (strncmp(buf, "regex", 5) != 0) continue; + if (strncmp(buf, "regex_posix", 11) == 0) { + regex_posix = 1; + p = &buf[11]; + } + else { + regex_posix = 0; + p = &buf[5]; + } + + while (*p && isspace((int)*p)) p++; + if (*p == '\0') continue; + q = p++; + while (*p && !isspace((int)*p)) p++; + if (*p == '\0') continue; + *p = '\0'; + + cern_format = squid_format = common_extended_format = 0; + + if (strcmp(q, argv[2]) == 0) { + if(regex_posix) { + + q = ++p; + while (*p && !isspace((int)*p)) p++; + if (*p == '\0') continue; + *p = '\0'; + fprintf(stderr, "args are (%s)\n", q); + if(strncmp(q, "method,size", 11) == 0) { + cern_format = 1; + methodpos = 1; + sizepos = 2; + } + else if(strncmp(q, "size,method", 11) == 0) { + methodpos = 2; + sizepos = 1; + } + else { + char *str; + int pos; + + pos = 1; + str=q; + do { + switch(str[0]) { + case '1': + methodpos = pos++; + break; + case '2': + sizepos = pos++; + break; + case '3': + c_statuspos = pos++; + break; + case '4': + s_statuspos = pos++; + break; + case '-': + methodpos = 1; + sizepos = 2; + str[0] = '\0'; + break; + case ',': + case '\0': + break; + default: + fprintf(stderr, + "could figure out arg order params (%s)\n", + str); + exit(1); + } + } while ( *str++ ); + + if(c_statuspos > 0 && s_statuspos > 0) { + if(strcmp(argv[2], "SQUID") == 0) + squid_format = 1; + else + common_extended_format = 1; + } else + cern_format = 1; + } + + fprintf(stderr, "cern: %d, cef: %d, squid: %d, MP: %d, SP: %d, CSP: %d, SSP: %d\n", + cern_format, common_extended_format, squid_format, + methodpos, sizepos, c_statuspos, s_statuspos); + } + + q = ++p; + while (*p && *p != '\n') p++; + while (p >= q && isspace((int)*p)) p--; + p[1] = '\0'; + if(regex_posix) { +#ifdef HAVE_REGCOMP + fprintf(stderr, "%s[%d]: regex_posix: %s\n", argv[1], lno, q); + fclose(fc); + if(regcomp(&re, q, REG_EXTENDED) != 0 ) { + fprintf(stderr, "Error: bad regular expression\n"); + exit(1); + } +#else + fprintf(stderr, "%s[%d]: no support for POSIX regexp\n", + argv[1], lno); +#endif + } + else { +#ifdef HAVE_REGCMP + if(strcmp(argv[2], "CERN") == 0) + cern_format = 1; + else if (strcmp(argv[2], "NS_PROXY") == 0) + common_extended_format = 1; + else if (strcmp(argv[2], "SQUID") == 0) + squid_format = 1; + + fprintf(stderr, "%s[%d]: regex: %s\n", argv[1], lno, q); + fclose(fc); + comp = regcmp(q, NULL); + if (comp == NULL) { + fprintf(stderr, "Error: bad regular expression\n"); + exit(1); + } +#else + fprintf(stderr, "%s[%d]: regcmp is not available\n", + argv[1], lno); +#endif + } + break; + } + } + + lno = 0; + remote_fetches = proxy_cache_hits = client_cache_hits = 0; + remote_bytes = proxy_bytes = 0.0; + while (fgets(buf, sizeof(buf), stdin) != NULL) { + lno++; + if(regex_posix) { +#ifdef HAVE_REGEXEC + if(regexec(&re, buf, nmatch, pmatch, 0) == 0) { + buf[pmatch[methodpos].rm_eo] = '\0'; + buf[pmatch[sizepos].rm_eo] = '\0'; + if(common_extended_format || squid_format) { + buf[pmatch[c_statuspos].rm_eo] = '\0'; + buf[pmatch[s_statuspos].rm_eo] = '\0'; + } + + if(common_extended_format) { + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, + &buf[pmatch[methodpos].rm_so], + &buf[pmatch[sizepos].rm_so], + &buf[pmatch[c_statuspos].rm_so], + &buf[pmatch[s_statuspos].rm_so]); + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + strcmp(&buf[pmatch[s_statuspos].rm_so], "200") == 0) { + fprintf(stderr,"\tREMOTE fetch of %.0f bytes\n", + atof(&buf[pmatch[sizepos].rm_so])); + remote_fetches++; + remote_bytes += atof(&buf[pmatch[sizepos].rm_so]); + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + (strcmp(&buf[pmatch[s_statuspos].rm_so], "304") == 0 || + strcmp(&buf[pmatch[s_statuspos].rm_so], "-") == 0)) { + fprintf(stderr,"\tCACHE return of %.0f bytes\n", + atof(&buf[pmatch[sizepos].rm_so])); + proxy_cache_hits++; + proxy_bytes += atof(&buf[pmatch[sizepos].rm_so]); + + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "304") == 0 && + (strcmp(&buf[pmatch[s_statuspos].rm_so], "304") == 0 || + strcmp(&buf[pmatch[s_statuspos].rm_so], "-") == 0)) { + fprintf(stderr,"\tCLIENT hit of %.0f bytes\n", + atof(&buf[pmatch[sizepos].rm_so])); + client_cache_hits++; + } + } else if(squid_format) { + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, + &buf[pmatch[methodpos].rm_so], + &buf[pmatch[sizepos].rm_so], + &buf[pmatch[c_statuspos].rm_so], + &buf[pmatch[s_statuspos].rm_so]); + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + (strstr(&buf[pmatch[s_statuspos].rm_so], + "_MISS")!=NULL || + strstr(&buf[pmatch[s_statuspos].rm_so], + "_CLIENT_REFRESH")!=NULL || + strstr(&buf[pmatch[s_statuspos].rm_so], + "_SWAPFAIL")!=NULL)){ + fprintf(stderr,"\tREMOTE fetch of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(&buf[pmatch[sizepos].rm_so]), + &buf[pmatch[c_statuspos].rm_so], &buf[pmatch[s_statuspos].rm_so]); + remote_fetches++; + remote_bytes += atof(&buf[pmatch[sizepos].rm_so]); + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "200") == 0 && + strstr(&buf[pmatch[s_statuspos].rm_so], "_HIT") != NULL) { + fprintf(stderr,"\tCACHE return of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(&buf[pmatch[sizepos].rm_so]), + &buf[pmatch[c_statuspos].rm_so], &buf[pmatch[s_statuspos].rm_so]); + proxy_cache_hits++; + proxy_bytes += atof(&buf[pmatch[sizepos].rm_so]); + + } + if(strcmp(&buf[pmatch[c_statuspos].rm_so], "304") == 0 && + strstr(&buf[pmatch[s_statuspos].rm_so], "_HIT") != NULL) { + fprintf(stderr,"\tCLIENT hit of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(&buf[pmatch[sizepos].rm_so]), + &buf[pmatch[c_statuspos].rm_so], &buf[pmatch[s_statuspos].rm_so]); + client_cache_hits++; + } + } else { + fprintf(stderr, "[%d] match: method=\"%s\" size=\"%s\"\n", lno, + &buf[pmatch[methodpos].rm_so], &buf[pmatch[sizepos].rm_so]); + } + } + else + fprintf(stderr, "[%d] no match: %s\n", lno, buf); +#else + fprintf(stderr, "[%d] - no regexec()\n", lno); +#endif + } + else { +#ifdef HAVE_REGEX + if (regex(comp, buf, sub0, sub1, sub2, sub3) != NULL) { + if(common_extended_format) { + + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, sub0, sub1, sub2, sub3); + + if(strcmp(sub2, "200") == 0 && + strcmp(sub3, "200") == 0 ) { + fprintf(stderr,"\tREMOTE fetch of %s bytes\n", sub1); + remote_fetches++; + remote_bytes += atof(sub1); + } + if(strcmp(sub2, "200") == 0 && + (strcmp(sub3, "304") == 0 || strcmp(sub3, "-") == 0)) { + fprintf(stderr,"\tCACHE return of %s bytes\n", sub1); + proxy_cache_hits++; + proxy_bytes += atof(sub1); + } + if(strcmp(sub2, "304") == 0 && + (strcmp(sub3, "304") == 0 || strcmp(sub3, "-") == 0)) { + fprintf(stderr,"\tCLIENT hit of %s bytes\n", sub1); + client_cache_hits++; + } + } else if(squid_format) { + + fprintf(stderr,"[%d] M: %s, S: %s, CS: %s, SS: %s\n", + lno, sub0, sub1, sub2, sub3); + + if(strcmp(sub2, "200") == 0 && + (strstr(sub3, "_MISS") != NULL || + strstr(sub3, "_CLIENT_REFRESH")!= NULL || + strstr(sub3, "_SWAPFAIL") != NULL)){ + + fprintf(stderr,"\tREMOTE fetch of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(sub1), + sub2, sub3); + + remote_fetches++; + remote_bytes += atof(sub1); + } + if(strcmp(sub2, "200") == 0 && + strstr(sub3, "_HIT") != NULL) { + + fprintf(stderr,"\tCACHE return of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(sub1), + sub2, sub3); + + proxy_cache_hits++; + proxy_bytes += atof(sub1); + + } + if(strcmp(sub2, "304") == 0 && + strstr(sub3, "_HIT") != NULL) { + + fprintf(stderr,"\tCLIENT hit of %.0f bytes (code: %s, Squid result code: %s)\n", + atof(sub3), + sub2, sub3); + + client_cache_hits++; + } + } else { + fprintf(stderr, "[%d] match: method=\"%s\" size=\"%s\"\n", lno, + sub0, sub1); + } + } + else + fprintf(stderr, "[%d] no match: %s\n", lno, buf); +#else + fprintf(stderr, "[%d] - no regex()\n", lno); +#endif + } + } + + if(common_extended_format || squid_format) { + fprintf(stderr,"Proxy Cache Summary Report\n\n"); + + fprintf(stderr, + "# requests %ld\n# client cache hits %ld\n# cache hits %ld\n# remote fetches %ld\n", + (client_cache_hits + proxy_cache_hits + remote_fetches), + client_cache_hits, proxy_cache_hits, remote_fetches); + fprintf(stderr, + "\nTotal Mbytes %f bytes\nFrom proxy cache %f Mbytes\nFrom remote sites %f Mbytes\n\n", + (proxy_bytes + remote_bytes)/1000000.0, + proxy_bytes/1000000.0, remote_bytes/1000000.0); + + fprintf(stderr, + "Client Cache %% hit rate: %.2f\n", + 100.0*(float)client_cache_hits/(float)(client_cache_hits + proxy_cache_hits + remote_fetches)); + fprintf(stderr, + "Proxy Cache %% hit rate: %.2f\n", + 100.0*(float)proxy_cache_hits/(float)(client_cache_hits + proxy_cache_hits + remote_fetches)); + fprintf(stderr, + "Local Cache %% hit rate: %.2f\n", + 100.0*(float)(client_cache_hits + proxy_cache_hits)/ + (float)(client_cache_hits + proxy_cache_hits + remote_fetches)); + + fprintf(stderr, + "\nAverage fetch size: Proxy -> Client: %.2f Kb\n", + proxy_bytes/proxy_cache_hits/1000.0); + fprintf(stderr, + "Average fetch size: Remote -> Client : %.2f Kb\n", + remote_bytes/remote_fetches/1000.0); + + fprintf(stderr,"\nClient Cache bandwidth reduction effectiveness: UNKNOWN\n"); + fprintf(stderr, + "Proxy Cache bandwidth reduction effectiveness: %f%%\n", + 100.0*proxy_bytes/(proxy_bytes + remote_bytes)); + + } + + exit(0); +} diff --git a/src/pmdas/weblog/help b/src/pmdas/weblog/help new file mode 100644 index 0000000..3a523e6 --- /dev/null +++ b/src/pmdas/weblog/help @@ -0,0 +1,654 @@ +# +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +@ web.config.numservers number of servers in configuration file +The number of Web servers specified in the configuration file. +The log files for these Web servers may or may not be monitored, +see web.perserver.watched and web.allservers.numwatched. + +@ web.config.catchup maximum time (secs) before Web server logs are probed +The time in seconds after which monitored Web server logs will be +examined, even if there have been no requests for performance metrics +from those logs. The "catch up" process spreads the load and minimizes +the latency at the first requests for metrics that have not been +requested for a long time. + +This metric has the initial value of the -t delay option to +pmdaweblog, and may be altered using pmStore(1). + +@ web.config.catchuptime time (secs) to perform catchup +Accumulated elapsed time in which the Web logs PMDA has been performing +the "catch up" process to examine all Web server logs. + +@ web.config.check time (secs) after which stationary logs will be re-opened +Web server log files that are not changing are periodically closed and +re-opened to detect possible log file rotation. This metric controls +how often a stationary log file will be re-opened. + +This metric has the initial value of the -n idlesec option to +pmdaweblog, and may be altered using pmStore(1). + +@ web.allservers.numwatched number of servers being monitored +The number of Web servers that are being monitored, as opposed +the number specified in the configuration file. + +See also web.config.numservers and web.perserver.watched. + +@ web.allservers.numalive number of watched servers that are alive +The number of servers that are being watchedly watched that have both +logs files. + +@ web.allservers.errors number of errors reported by all watched servers +The number of errors reported by all watched servers. + +@ web.allservers.requests.total requests processed by all servers +The total number of HTTP requests processed by all watched servers. + +@ web.allservers.bytes.total bytes sent by all servers +The total number of bytes sent by all watched servers. + +@ web.allservers.requests.get GET requests handled by all watched servers +The number of HTTP GET requests that were processed by all watched servers. + +@ web.allservers.bytes.get bytes sent in reply by all servers to GET requests +The number of bytes that have been sent by all watched servers in reply +to HTTP GET requests. + +@ web.allservers.requests.head HEAD requests handled by all watched servers +The number of HTTP HEAD requests that were processed by all watched +servers. + +@ web.allservers.bytes.head bytes sent in reply by all servers to HEAD requests +The number of bytes that have been sent by all watched servers in reply +to HTTP HEAD requests. + +@ web.allservers.requests.post POST requests handled by all watched servers +The number of HTTP POST requests that were processed by all watched +servers. + +@ web.allservers.bytes.post bytes sent in reply by all servers to POST requests +The number of bytes that have been sent by all watched servers in reply +to HTTP POST requests. + +@ web.allservers.requests.other other requests handled by all watch servers +The number of HTTP requests, other than GET, HEAD and POST, that were +processed by all watched servers. + +@ web.allservers.bytes.other bytes sent in reply by servers to other requests +The number of bytes that have been sent by this server in reply to HTTP +requests other than GET, HEAD or POST. + +@ web.allservers.requests.size.zero replies of 0 bytes sent by all servers +The total number of HTTP requests that required a response of 0 bytes from +all watched servers. + +@ web.allservers.bytes.size.zero total bytes sent in 0k replies +The total number of bytes sent in replies of 0k by all watched servers. +This metric is always zero and is provided for consistency only. + +@ web.allservers.requests.size.le3k replies of <= 3k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 3k from all watched servers. + +@ web.allservers.bytes.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent in replies of less than or equal to 3k in +size by all watched servers. + +@ web.allservers.requests.size.le10k replies of <= 10k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 10k from all watched servers. + +@ web.allservers.bytes.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent in replies of less than or equal to 10k in +size by all watched servers. + +@ web.allservers.requests.size.le30k replies of <= 30k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 30k from all watched servers. + +@ web.allservers.bytes.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent in replies of less than or equal to 30k in +size by all watched servers. + +@ web.allservers.requests.size.le100k replies of <= 100k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 100k from all watched servers. + +@ web.allservers.bytes.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent in replies of less than or equal to 100k in +size by all watched servers. + +@ web.allservers.requests.size.le300k replies of <= 300k sent by all servers +The number of HTTP requests that required a response of less than or equal +to 300k from all watched servers. + +@ web.allservers.bytes.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent in replies of less than or equal to 300k in +size by all watched servers. + +@ web.allservers.requests.size.le1m replies of <= 1M sent by all servers +The number of HTTP requests that required a response of less than or equal +to 1M from all watched servers. + +@ web.allservers.bytes.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent in replies of less than or equal to 1M in +size by all watched servers. + +@ web.allservers.requests.size.le3m replies of <= 3M sent by all servers +The number of HTTP requests that required a response of less than or equal +to 3M from all watched servers. + +@ web.allservers.bytes.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent in replies of less than or equal to 3M in +size by all watched servers. + +@ web.allservers.requests.size.gt3m replies of > 3M sent by all servers +The number of HTTP requests that required a response of greater than +3M from all watched servers. + +@ web.allservers.bytes.size.gt3m total bytes sent > 3M replies +The total number of bytes sent in replies of greater than 3M in size by +all watched servers. + +@ web.allservers.requests.size.unknown replies of unknown size by all servers +The number of HTTP requests that required a response of unknown size +from all watched servers. + +@ web.allservers.requests.client.total requests satisfied by client caches for all cacheing servers +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from cache (and remote if checked). These are client cache hits. + +@ web.allservers.requests.cached.total requests satisfied by server caches for all cacheing servers +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from the remote site or were deemed cache hits via other +mechanisms such as recency. These are server cache hits and result in +data transferred from cache to client. + +@ web.allservers.requests.cached.size.zero replies of 0 bytes sent by all caches +The number of HTTP GET cache hits that required a response of 0 bytes from +all watched caches. + +@ web.allservers.requests.cached.size.le3k replies of <= 3k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 3k from all watched caches. + +@ web.allservers.requests.cached.size.le10k replies of <= 10k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 10k from all watched caches. + +@ web.allservers.requests.cached.size.le30k replies of <= 30k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 30k from all watched caches. + +@ web.allservers.requests.cached.size.le100k replies of <= 100k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 100k from all watched caches. + +@ web.allservers.requests.cached.size.le300k replies of <= 300k sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 300k from all watched caches. + +@ web.allservers.requests.cached.size.le1m replies of <= 1M sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 1M from all watched caches. + +@ web.allservers.requests.cached.size.le3m replies of <= 3M sent by all caches +The number of HTTP GET cache hits that required a response of less than +or equal to 3M from all watched caches. + +@ web.allservers.requests.cached.size.gt3m replies of > 3M sent by all caches +The number of HTTP GET cache hits that required a response of greater than +3M from all watched caches. + +@ web.allservers.requests.cached.size.unknown replies of unknown size by all caches +The number of HTTP GET cache hits that required a response of unknown +size from all watched caches. + +@ web.allservers.requests.uncached.total requests satisfied by remote server for all cacheing servers +The total number of HTTP GET/IMS requests that resulted in a real data +transfer from the remote server. These are either cache misses, or the +remote file had been modified since the cache entry was made. + +@ web.allservers.requests.uncached.size.zero replies of 0 bytes sent by all caches +The number of HTTP GET remote fetches that required a response of 0 +bytes from all watched caches. + +@ web.allservers.requests.uncached.size.le3k replies of <= 3k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 3k through all watched caches. + +@ web.allservers.requests.uncached.size.le10k replies of <= 10k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 10k through all watched caches. + +@ web.allservers.requests.uncached.size.le30k replies of <= 30k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 30k through all watched caches. + +@ web.allservers.requests.uncached.size.le100k replies of <= 100k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 100k through all watched caches. + +@ web.allservers.requests.uncached.size.le300k replies of <= 300k sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 300k through all watched caches. + +@ web.allservers.requests.uncached.size.le1m replies of <= 1M sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 1M through all watched caches. + +@ web.allservers.requests.uncached.size.le3m replies of <= 3M sent by all caches +The number of HTTP GET remote fetches that required a response of less +than or equal to 3M through all watched caches. + +@ web.allservers.requests.uncached.size.gt3m replies of > 3M sent by all caches +The number of HTTP GET remote fetches that required a response of greater +than 3M through all watched caches. + +@ web.allservers.requests.uncached.size.unknown replies of unknown size by all caches +The number of HTTP GET requests that required a response of unknown size +through all watched caches. + +@ web.allservers.bytes.cached.total bytes sent by caches as a result of cache hits for all cacheing servers +The total number of bytes sent to client due to HTTP GET/IMS requests +that resulted in "Not Modified"responses from the remote site or were +deemed cache hits via other mechanisms such as recency. + +@ web.allservers.bytes.cached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client by cache hit replies of 0k +by all watched caches. This metric is always zero and is provided for +consistency only. + +@ web.allservers.bytes.cached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 10k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 30k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 100k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 300k in size by all watched caches. + +@ web.allservers.bytes.cached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 1M in size by all watched caches. + +@ web.allservers.bytes.cached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3M in size by all watched caches. + +@ web.allservers.bytes.cached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client by cache hit replies of greater +than 3M in size by all watched caches. + +@ web.allservers.bytes.uncached.total bytes sent by remote servers as a result of cache misses for all cacheing servers +The total number of bytes sent to client from the remote server. These +are either cache misses, or the remote file had been modified since +the cache entry was made. + +@ web.allservers.bytes.uncached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client from the remote server of 0k +by all watched caches. This metric is always zero and is provided for +consistency only. + +@ web.allservers.bytes.uncached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client from the remote server of less +than or equal to 3k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client from the remote server of less +than or equal to 10k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client from the remote server of less +than or equal to 30k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client from the remote server of less +than or equal to 100k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client from the remote server of less +than or equal to 300k in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client from the remote server of less +than or equal to 1M in size by all watched caches. + +@ web.allservers.bytes.uncached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client from the remote server of less +than or equal to 3M in size by all watched caches. + +@ web.allservers.bytes.uncached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client from the remote server of +greater than 3M in size by all watched caches. + +@ web.perserver.watched flag set to 1 if monitoring this server +A flag which is set to 1 if this server is being monitored. +This metric may be altered using pmStore(1). + +@ web.perserver.numlogs number of readable log files for this server +The number of log files that are readable for the server. + +@ web.perserver.errors number of logged errors by this server +The number of errors (and other administrative messages) that have been +logged in the error log by this server. + +@ web.perserver.requests.total requests processed by this server +The number of HTTP requests processed by this server. + +@ web.perserver.bytes.total bytes sent by this server +The number of bytes this server has sent. + +@ web.perserver.requests.get GET requests handled by server +The number of HTTP GET requests that were processed by this server. + +@ web.perserver.bytes.get bytes sent in reply to GET requests +The number of bytes that have been sent by this server in reply to HTTP +GET requests. + +@ web.perserver.requests.head HEAD requests handled by server +The number of HTTP HEAD requests that were processed by this server. + +@ web.perserver.bytes.head bytes sent in reply to HEAD requests +The number of bytes that have been sent by this server in reply to HTTP +HEAD requests. + +@ web.perserver.requests.post POST requests handled by server +The number of HTTP POST requests that were processed by this server. + +@ web.perserver.bytes.post bytes sent in reply to POST requests +The number of bytes that have been sent by this server in reply to HTTP +POST requests. + +@ web.perserver.requests.other other requests handled by server +The number of HTTP requests, other than GET, HEAD and POST, that were +processed by this server. + +@ web.perserver.bytes.other bytes sent in reply to other requests +The number of bytes that have been sent by this server in reply to HTTP +requests other than GET, HEAD or POST. + +@ web.perserver.requests.size.zero requests requiring 0 bytes in reply +The number of HTTP requests that required a response of 0 bytes from this +server. + +@ web.perserver.bytes.size.zero total bytes sent in 0k replies. +The total number of bytes sent in replies of 0k by this server. This metric +is always 0 and is provided for consistency only. + +@ web.perserver.requests.size.le3k requests requiring <= 3k replies +The number of HTTP requests that required a response of less than or equal +to 3k from this server. + +@ web.perserver.bytes.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent in replies of less than or equal to 3k in +size. + +@ web.perserver.requests.size.le10k requests requiring <= 10k replies +The number of HTTP requests that required a response of less than or equal +to 10k from this server. + +@ web.perserver.bytes.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent in replies of less than or equal to 10k in +size. + +@ web.perserver.requests.size.le30k requests requiring <= 30k replies +The number of HTTP requests that required a response of less than or equal +to 30k from this server. + +@ web.perserver.bytes.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent in replies of less than or equal to 30k in +size. + +@ web.perserver.requests.size.le100k requests requiring <= 100k replies +The number of HTTP requests that required a response of less than or equal +to 100k from this server. + +@ web.perserver.bytes.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent in replies of less than or equal to 100k in +size. + +@ web.perserver.requests.size.le300k requests requiring <= 300k replies +The number of HTTP requests that required a response of less than or equal +to 300k from this server. + +@ web.perserver.bytes.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent in replies of less than or equal to 300k in +size. + +@ web.perserver.requests.size.le1m requests requiring <= 1M replies +The number of HTTP requests that required a response of less than or equal +to 1M from this server. + +@ web.perserver.bytes.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent in replies of less than or equal to 1M in +size. + +@ web.perserver.requests.size.le3m requests requiring <= 3M replies +The number of HTTP requests that required a response of less than or equal +to 3M from this server. + +@ web.perserver.bytes.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent in replies of less than or equal to 3M in +size. + +@ web.perserver.requests.size.gt3m requests requiring > 3M replies +The number of HTTP requests that required a response of greater than +3M from this server. + +@ web.perserver.bytes.size.gt3m total bytes sent > 3M replies +The total number of bytes sent in replies of greater than 3M in size. + +@ web.perserver.requests.size.unknown requests of unknown size +The number of HTTP requests that required a response of unknown size from +this server. + +@ web.perserver.logidletime seconds since log last modified +The number of seconds since the access log for this server was modified. + +@ web.perserver.requests.client.total requests satisfied by client caches for this cache +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from cache (and remote if checked). These are client cache hits. + +@ web.perserver.requests.cached.total requests satisfied by server caches for this cache +The total number of HTTP GET/IMS requests that resulted in "Not Modified" +responses from the remote site or were deemed cache hits via other +mechanisms such as recency. These are server cache hits and result in +data transferred from cache to client. + +@ web.perserver.requests.cached.size.zero replies of 0 bytes sent by this cache +The number of HTTP GET cache hits that required a response of 0 bytes from +this cache. + +@ web.perserver.requests.cached.size.le3k replies of <= 3k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 3k from this cache. + +@ web.perserver.requests.cached.size.le10k replies of <= 10k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 10k from this cache. + +@ web.perserver.requests.cached.size.le30k replies of <= 30k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 30k from this cache. + +@ web.perserver.requests.cached.size.le100k replies of <= 100k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 100k from this cache. + +@ web.perserver.requests.cached.size.le300k replies of <= 300k sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 300k from this cache. + +@ web.perserver.requests.cached.size.le1m replies of <= 1M sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 1M from this cache. + +@ web.perserver.requests.cached.size.le3m replies of <= 3M sent by this cache +The number of HTTP GET cache hits that required a response of less than +or equal to 3M from this cache. + +@ web.perserver.requests.cached.size.gt3m replies of > 3M sent by this cache +The number of HTTP GET cache hits that required a response of greater than +3M from this cache. + +@ web.perserver.requests.cached.size.unknown replies of unknown size by this cache +The number of HTTP GET cache hits that required a response of unknown +size from this cache. + +@ web.perserver.requests.uncached.total requests satisfied by remote server for this cache +The total number of HTTP GET/IMS requests that resulted in a real data +transfer from the remote server. These are either cache misses, or the +remote file had been modified since the cache entry was made. + +@ web.perserver.requests.uncached.size.zero replies of 0 bytes sent by this cache +The number of HTTP GET remote fetches that required a response of 0 +bytes from this cache. + +@ web.perserver.requests.uncached.size.le3k replies of <= 3k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 3k through this cache. + +@ web.perserver.requests.uncached.size.le10k replies of <= 10k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 10k through this cache. + +@ web.perserver.requests.uncached.size.le30k replies of <= 30k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 30k through this cache. + +@ web.perserver.requests.uncached.size.le100k replies of <= 100k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 100k through this cache. + +@ web.perserver.requests.uncached.size.le300k replies of <= 300k sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 300k through this cache. + +@ web.perserver.requests.uncached.size.le1m replies of <= 1M sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 1M through this cache. + +@ web.perserver.requests.uncached.size.le3m replies of <= 3M sent by this cache +The number of HTTP GET remote fetches that required a response of less +than or equal to 3M through this cache. + +@ web.perserver.requests.uncached.size.gt3m replies of > 3M sent by this cache +The number of HTTP GET remote fetches that required a response of greater +than 3M through this cache. + +@ web.perserver.requests.uncached.size.unknown replies of unknown size by this cache +The number of HTTP GET requests that required a response of unknown size +through this cache. + +@ web.perserver.bytes.cached.total bytes sent by caches as a result of cache hits for this cache +The total number of bytes sent to client due to HTTP GET/IMS requests +that resulted in "Not Modified"responses from the remote site or were +deemed cache hits via other mechanisms such as recency. + +@ web.perserver.bytes.cached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client by cache hit replies of 0k by +this cache. This metric is always zero and is provided for consistency +only. + +@ web.perserver.bytes.cached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3k in size by this cache. + +@ web.perserver.bytes.cached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 10k in size by this cache. + +@ web.perserver.bytes.cached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 30k in size by this cache. + +@ web.perserver.bytes.cached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 100k in size by this cache. + +@ web.perserver.bytes.cached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 300k in size by this cache. + +@ web.perserver.bytes.cached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 1M in size by this cache. + +@ web.perserver.bytes.cached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client by cache hit replies of less +than or equal to 3M in size by this cache. + +@ web.perserver.bytes.cached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client by cache hit replies of greater +than 3M in size by this cache. + +@ web.perserver.bytes.uncached.total bytes sent by remote servers as a result of cache misses for this cache +The total number of bytes sent to client from the remote server. These +are either cache misses, or the remote file had been modified since +the cache entry was made. + +@ web.perserver.bytes.uncached.size.zero total bytes sent in 0k replies +The total number of bytes sent to client from the remote server of 0k by +this cache. This metric is always zero and is provided for consistency +only. + +@ web.perserver.bytes.uncached.size.le3k total bytes sent in <= 3k replies +The total number of bytes sent to client from the remote server of less +than or equal to 3k in size by this cache. + +@ web.perserver.bytes.uncached.size.le10k total bytes sent in <= 10k replies +The total number of bytes sent to client from the remote server of less +than or equal to 10k in size by this cache. + +@ web.perserver.bytes.uncached.size.le30k total bytes sent in <= 30k replies +The total number of bytes sent to client from the remote server of less +than or equal to 30k in size by this cache. + +@ web.perserver.bytes.uncached.size.le100k total bytes sent in <= 100k replies +The total number of bytes sent to client from the remote server of less +than or equal to 100k in size by this cache. + +@ web.perserver.bytes.uncached.size.le300k total bytes sent in <= 300k replies +The total number of bytes sent to client from the remote server of less +than or equal to 300k in size by this cache. + +@ web.perserver.bytes.uncached.size.le1m total bytes sent in <= 1M replies +The total number of bytes sent to client from the remote server of less +than or equal to 1M in size by this cache. + +@ web.perserver.bytes.uncached.size.le3m total bytes sent in <= 3M replies +The total number of bytes sent to client from the remote server of less +than or equal to 3M in size by this cache. + +@ web.perserver.bytes.uncached.size.gt3m total bytes sent > 3M replies +The total number of bytes sent to client from the remote server of +greater than 3M in size by this cache. + diff --git a/src/pmdas/weblog/pmda.c b/src/pmdas/weblog/pmda.c new file mode 100644 index 0000000..6c1967f --- /dev/null +++ b/src/pmdas/weblog/pmda.c @@ -0,0 +1,1205 @@ +/* + * Web PMDA, based on generic driver for a daemon-based PMDA + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "weblog.h" +#include "domain.h" +#if defined(HAVE_REGEX_H) +#include +#endif +#if defined(HAVE_SYS_WAIT_H) +#include +#endif +#if defined(HAVE_SCHED_H) +#include +#endif + +#ifdef IS_SOLARIS +#define CLONE_VM 0x00000100 +#elif !defined(CLONE_VM) +#define CLONE_VM 0x0 +#endif + +#ifndef HAVE_SPROC +int sproc (void (*entry) (void *), int flags, void *arg); +#endif + +/* path to the configuration file */ +static char *configFileName = (char*)0; + +/* line number of configuration file */ +static int line = 0; + +/* number of errors in configuration file */ +static int err = 0; + +/* configured for num of servers */ +__uint32_t wl_numServers = 0; + +/* number of active servers */ +__uint32_t wl_numActive = 0; + +/* check logs every 15 seconds by default */ +__uint32_t wl_refreshDelay = 15; + +/* re-open logs if unchanged in this number of seconds */ +__uint32_t wl_chkDelay = 20; + +/* max servers per sproc */ +__uint32_t wl_sprocThresh = 80; + +/* number of sprocs spawned */ +__uint32_t wl_numSprocs = 0; + +/* number of regex parsed */ +__uint32_t wl_numRegex = 0; + +/* list of web servers */ +WebServer *wl_servers = (WebServer*)0; + +/* list of regular expressions */ +WebRegex *wl_regexTable = (WebRegex*)0; + +/* instance table of web servers */ +pmdaInstid *wl_serverInst = (pmdaInstid*)0; + +/* list of sprocs spawned from the main process */ +WebSproc *wl_sproc; + +/* default name for log file */ +char *wl_logFile = "weblog.log"; + +/* default path to help file */ +char wl_helpFile[MAXPATHLEN]; + +/* default user name for PMDA */ +char *wl_username; + +/* + * Usage Information + */ + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s [options] configfile\n\ +\n\ +Options\n\ + -C check configuration and exit\n\ + -d domain PMDA domain number\n\ + -h helpfile get help text from helpfile rather than default path\n\ + -i port expect PMCD to connect on given inet port (number or name)\n\ + -l logfile redirect diagnostics and trace output (default weblog.log)\n\ + -n idlesec number of seconds of weblog inactivity before checking for\n\ + log rotation\n\ + -p expect PMCD to supply stdin/stdout (pipe)\n\ + -S num number of web servers per sproc\n\ + -t delay maximum number of seconds between reading weblog files\n\ + -u socket expect PMCD to connect on given unix domain socket\n\ + -U username user account to run under (default \"pcp\")\n\ + -6 port expect PMCD to connect on given ipv6 port (number or name)\n\ +\n\ +If none of the -i, -p or -u options are given, the configuration file is\n\ +checked and then %s terminates.\n", pmProgname, pmProgname); + exit(1); +} + +void +logmessage(int priority, const char *format, ...) +{ + va_list arglist; + char buffer[2048]; + char *level; + char *p; + time_t now; + + buffer[0] = '\0'; + time(&now); + + switch (priority) { + case LOG_EMERG : + level = "Emergency"; + break; + case LOG_ALERT : + level = "Alert"; + break; + case LOG_CRIT : + level = "Critical"; + break; + case LOG_ERR : + level = "Error"; + break; + case LOG_WARNING : + level = "Warning"; + break; + case LOG_NOTICE : + level = "Notice"; + break; + case LOG_INFO : + level = "Info"; + break; + case LOG_DEBUG : + level = "Debug"; + break; + default: + level = "???"; + break; + } + + va_start (arglist, format); + vsnprintf (buffer, sizeof(buffer), format, arglist); + for (p = buffer; *p; p++); + if (*(--p) == '\n') *p = '\0'; + fprintf (stderr, "[%.19s] %s(%" FMT_PID ") %s: %s\n", ctime(&now), pmProgname, getpid(), level, buffer) ; + va_end (arglist) ; +} + +/* + * Errors message during parsing of config file + */ + +static void +yyerror(char *s) +{ + fprintf(stderr, "[%s:%d] Error: %s\n", configFileName, line, s); + err++; +} + +/* + * Warning message during parsing of config file + */ + +static void +yywarn(char *s) +{ + fprintf(stderr, "[%s:%d] Warning: %s\n", configFileName, line, s); +} + +/* + * skip remaining characters on this line + */ + +static void +skip_to_eol(FILE *f) +{ + int c; + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + return; + } + return; +} + +/* + * Are we at the end of the line (sucks up spaces and tabs which may preceed + * EOL) + */ + +static void +check_to_eol(FILE *f) +{ + int c; + int i = 0; + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; + if (c == ' ' || c == '\t') + continue; + i++; + } + if (i) + yywarn("additional words in line, ignored"); + + return; +} + +/* + * Get a word. A word if any text until a whitespace + */ + +static int +getword(FILE *f, char *buf, int len) +{ + int c; + char *bend = &buf[len-1]; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + continue; + ungetc(c, f); + break; + } + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + break; + if (c == '\n') { + ungetc(c, f); + break; + } + if (buf < bend) { + *buf++ = c; + continue; + } + else { + yyerror("word too long, remainder of line ignored"); + return -1; + } + } + *buf = '\0'; + + return c == EOF ? 0 : 1; +} + +/* + * Get the next line from buffer + */ + +static int +get_to_eol(FILE *f, char *buf, int len) +{ + int c; + char *bend = &buf[len-1]; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ' || c == '\t') + continue; + ungetc(c, f); + break; + } + + while ((c = fgetc(f)) != EOF) { + if (c == '\n') + break; + if (buf < bend) { + *buf++ = c; + continue; + } + else { + yyerror("list of words too long, remainder of line ignored"); + return -1; + } + } + *buf = '\0'; + return c == EOF ? 0 : 1; +} + +/* + * Replacement for pmdaMainLoop + * Has a select loop on pipe from PMCD, reads in PDUs and acts on them + * appropriately. + */ + +static void +receivePDUs(pmdaInterface *dispatch) +{ + int nfds = 0; + time_t interval = 0; + int sts = 0; + struct timeval timeout; + fd_set rfds; + + + FD_ZERO(&rfds); + nfds = fileno(stdin)+1; + + for (;;) { + + FD_SET(fileno(stdin), &rfds); + __pmtimevalNow(&timeout); + timeout.tv_usec = 0; + interval = (time_t)wl_refreshDelay - (timeout.tv_sec % (time_t)wl_refreshDelay); + timeout.tv_sec = interval; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Select set for %d seconds\n", + interval); +#endif + + sts = select(nfds, &rfds, (fd_set*)0, (fd_set*)0, &timeout); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch select: %s", netstrerror()); + exit(1); + } + + if (sts == 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Select timed out\n"); +#endif + + refreshAll(); + continue; + } + + if (__pmdaMainPDU(dispatch) < 0){ + exit(1); + } + + if (interval == 0) { + refreshAll(); + } + + } +} + +/* + * Catch an SPROC dying, report what we know, and exit + * -- when main exits, other sprocs will get SIGHUP and exit quietly + */ +static void +onchld(int dummy) +{ + int done; + int waitStatus; + int sprocNum; + + while ((done = waitpid(-1, &waitStatus, WNOHANG)) > 0) { + for (sprocNum = 1; + wl_sproc[sprocNum].pid != done && sprocNum <= wl_numSprocs; + sprocNum++); + + if (sprocNum > wl_numSprocs) + { + logmessage(LOG_INFO, + "Unexpected child process (pid=%d) died!\n", + done); + continue; + } + + if (WIFEXITED(waitStatus)) { + + if (WEXITSTATUS(waitStatus) == 0) + logmessage(LOG_INFO, + "Sproc %d (pid=%d) exited normally\n", + sprocNum, done); + else + logmessage(LOG_INFO, + "Sproc %d (pid=%d) exited with status = %d\n", + sprocNum, done, WEXITSTATUS(waitStatus)); + } + else if (WIFSIGNALED(waitStatus)) { + +#ifdef WCOREDUMP + if (WCOREDUMP(waitStatus)) + logmessage(LOG_INFO, + "Sproc %d (pid=%d) received signal = %d and dumped core\n", + sprocNum, done, WTERMSIG(waitStatus)); +#endif + logmessage(LOG_INFO, + "Sproc %d (pid=%d) received signal = %d\n", + sprocNum, done, WTERMSIG(waitStatus)); + } + else { + logmessage(LOG_INFO, + "Sproc %d (pid=%d) died, reason unknown\n", + sprocNum, done); + } + + logmessage(LOG_INFO, + "Sproc %d managed servers %d to %d\n", + sprocNum, + wl_sproc[sprocNum].firstServer, + wl_sproc[sprocNum].lastServer); + } + + logmessage(LOG_INFO, "Main process exiting\n"); + exit(0); +} + +/* + * Parse command line args and the configuration file. Also sets up and fires + * off the required sprocs + */ + +int +main(int argc, char **argv) +{ + WebServer *server = (WebServer *)0; + WebSproc *proc = (WebSproc *)0; + + char *endnum = (char*)0; + char buf1[FILENAME_MAX]; + char buf2[FILENAME_MAX]; + char emess[120]; + char *pstart, *pend; + char argsDone, argFound; + char *err_msg; + + int i = 0; + int argCount = 0; + int checkOnly = 0; + int sts = 0; + int sep = __pmPathSeparator(); + int n = 0; + int serverTableSize = 0; + int regexTableSize = 0; + + FILE *configFile = (FILE*)0; + FILE *tmpFp = (FILE*)0; + + pmdaInterface desc; + struct timeval delta; + + struct { + int *argPos; + char *argString; + } regexargs[2]; + +#ifdef PCP_DEBUG + struct timeval start; + struct timeval end; + double startTime; +#endif + + __pmSetProgname(argv[0]); + __pmGetUsername(&wl_username); + +#ifdef PCP_DEBUG + __pmtimevalNow(&start); +#endif + + wl_isDSO = 0; + + snprintf(wl_helpFile, sizeof(wl_helpFile), "%s%c" "weblog" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&desc, PMDA_INTERFACE_2, pmProgname, WEBSERVER, + wl_logFile, wl_helpFile); + + while ((n = pmdaGetOpt(argc, argv, "CD:d:h:i:l:n:pS:t:u:U:6:?", + &desc, &err)) != EOF) { + switch (n) { + + case 'C': + checkOnly = 1; + break; + + case 'S': + wl_sprocThresh = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0') { + fprintf(stderr, "%s: -S requires numeric argument\n", + pmProgname); + err++; + } + break; + + case 'n': + if (pmParseInterval(optarg, &delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -n requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + else { + wl_chkDelay = delta.tv_sec; + } + break; + + case 't': + if (pmParseInterval(optarg, &delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -t requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + else { + wl_refreshDelay = delta.tv_sec; + } + break; + + case 'U': + wl_username = optarg; + break; + + default: + fprintf(stderr, "%s: Unknown option \"-%c\"", pmProgname, (char)n); + err++; + break; + } + } + + if (err || optind != argc-1) { + usage(); + } + + line = 0; + configFileName = argv[optind]; + configFile = fopen(configFileName, "r"); + + if (configFile == (FILE*)0) { + fprintf(stderr, "Unable to open config file %s\n", configFileName); + usage(); + } + + if (checkOnly == 0) { + /* + * if doing more than just parsing, force errors from here + * on into the logfile + */ + pmdaOpenLog(&desc); + __pmSetProcessIdentity(wl_username); + } + + /* + * Parse the configuration file + */ + + /* These settings should be reflected below */ + regexargs[0].argString = strdup("method"); + regexargs[1].argString = strdup("size"); + + while(!feof(configFile)) { + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts == 0) { + /* End of File */ + break; + } + + line++; + + if (sts < 0) { + /* error, reported in getword() */ + skip_to_eol(configFile); + continue; + } + + if (buf1[0] == '\0' || buf1[0] == '#') { + /* comment, or nothing in the line, next line please */ + skip_to_eol(configFile); + continue; + } + + if (strcasecmp(buf1, "regex_posix") == 0) { + /* + * Parse a regex specification + */ + + if (wl_numRegex == regexTableSize) { + regexTableSize += 2; + wl_regexTable = (WebRegex*)realloc(wl_regexTable, + regexTableSize * sizeof(WebRegex)); + if (wl_regexTable == (WebRegex*)0) { + __pmNoMem("main.wl_regexInst", + (wl_numRegex + 1) * sizeof(WebRegex), + PM_FATAL_ERR); + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex name"); + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].name = strdup(buf1); + + if (wl_numRegex) { + for (n = 0; n < wl_numRegex; n++) { + if (strcmp(wl_regexTable[n].name, + wl_regexTable[wl_numRegex].name) == 0) { + + snprintf(emess, sizeof(emess), "duplicate regex name (%s)", + wl_regexTable[wl_numRegex].name); + yyerror(emess); + break; + } + } + if (n < wl_numRegex) { + skip_to_eol(configFile); + continue; + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex match parameters"); + skip_to_eol(configFile); + continue; + } + + regexargs[0].argPos = &(wl_regexTable[wl_numRegex].methodPos); + regexargs[1].argPos = &(wl_regexTable[wl_numRegex].sizePos); + wl_regexTable[wl_numRegex].methodPos = 0; + wl_regexTable[wl_numRegex].sizePos = 0; + wl_regexTable[wl_numRegex].sizePos = 0; + wl_regexTable[wl_numRegex].s_statusPos = 0; + + pstart = buf1; + argCount = 0; + do { + argFound = 0; + argsDone = 1; + argCount++; + for(pend = pstart; *pend; pend++) { + if(*pend == ',') { + *pend = '\0'; + argsDone = 0; + break; + } + } + for(i = 0; i < sizeof(regexargs) / sizeof(regexargs[0]); i++) { + if(strcmp(pstart, regexargs[i].argString) == 0) { + *regexargs[i].argPos = argCount; + argFound = 1; + break; + } + } + if(!argFound) { + /* not the old method,size style */ + switch(pstart[0]) { + case '1': + wl_regexTable[wl_numRegex].methodPos = argCount; + argFound = 1; + break; + case '2': + wl_regexTable[wl_numRegex].sizePos = argCount; + argFound = 1; + break; + case '3': + wl_regexTable[wl_numRegex].c_statusPos = argCount; + argFound = 1; + break; + case '4': + wl_regexTable[wl_numRegex].s_statusPos = argCount; + argFound = 1; + break; + case '-': + wl_regexTable[wl_numRegex].methodPos = argCount++; + wl_regexTable[wl_numRegex].sizePos = argCount; + argFound = 1; + argsDone = 1; + break; + default: + break; + } + } + pstart = pend + 1; + } while(argsDone == 0 && argFound != 0); + + if(argFound == 0) { + yyerror("invalid keyword in regex match parameters"); + skip_to_eol(configFile); + continue; + } + + sts = get_to_eol(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex"); + else + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].regex = malloc(sizeof(*wl_regexTable[wl_numRegex].regex)); + if(wl_regexTable[wl_numRegex].regex == NULL) { + __pmNoMem("main.wl_regex", + sizeof(*wl_regexTable[wl_numRegex].regex), + PM_FATAL_ERR); + } + + if (regcomp(wl_regexTable[wl_numRegex].regex, buf1, REG_EXTENDED) != 0) { + yyerror("unable to compile regex"); + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%d regex %s: %s\n", + wl_numRegex, wl_regexTable[wl_numRegex].name, buf1); +#endif + + wl_regexTable[wl_numRegex].posix_regexp = 1; + wl_numRegex++; + } +#ifdef NON_POSIX_REGEX + else if (strcasecmp(buf1, "regex") == 0) { + /* + * Parse a regex specification + */ + + if (wl_numRegex == regexTableSize) { + regexTableSize += 2; + wl_regexTable = (WebRegex*)realloc(wl_regexTable, + regexTableSize * sizeof(WebRegex)); + if (wl_regexTable == (WebRegex*)0) { + __pmNoMem("main.wl_regexInst", + (wl_numRegex + 1) * sizeof(WebRegex), + PM_FATAL_ERR); + } + } + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex name"); + skip_to_eol(configFile); + continue; + } + + wl_regexTable[wl_numRegex].name = strdup(buf1); + + if (wl_numRegex) { + for (n = 0; n < wl_numRegex; n++) { + if (strcmp(wl_regexTable[n].name, + wl_regexTable[wl_numRegex].name) == 0) { + + snprintf(emess, sizeof(emess), "duplicate regex name (%s)", + wl_regexTable[wl_numRegex].name); + yyerror(emess); + break; + } + } + if (n < wl_numRegex) { + skip_to_eol(configFile); + continue; + } + } + + sts = get_to_eol(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract regex"); + else + skip_to_eol(configFile); + continue; + } + + if(strstr(buf1, "$2") != NULL && strstr(buf1, "$3") != NULL ) { + /* + * extended caching server format + * + * although these aren't used in the non-regex code, they + * are a good enough placeholder until server->counts.extendedp + * is set below + */ + wl_regexTable[wl_numRegex].c_statusPos = 1; + wl_regexTable[wl_numRegex].s_statusPos = 1; + + } + wl_regexTable[wl_numRegex].np_regex = regcmp(buf1, (char*)0); + + if (wl_regexTable[wl_numRegex].np_regex == (char*)0) { + yyerror("unable to compile regex"); + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%d NON POSIX regex %s: %s\n", + wl_numRegex, wl_regexTable[wl_numRegex].name, buf1); +#endif + + wl_regexTable[wl_numRegex].posix_regexp = 0; + wl_numRegex++; + } +#endif + else if (strcasecmp(buf1, "server") == 0) { + /* + * Parse a server specification + */ + + if (wl_numServers == serverTableSize) { + serverTableSize += 4; + wl_serverInst = (pmdaInstid*)realloc(wl_serverInst, + serverTableSize * sizeof(pmdaInstid)); + if (wl_serverInst == (pmdaInstid*)0) { + __pmNoMem("main.wl_serverInst", + (wl_numServers + 1) * sizeof(pmdaInstid), + PM_FATAL_ERR); + } + + wl_servers = (WebServer*)realloc(wl_servers, + serverTableSize * sizeof(WebServer)); + if (wl_servers == (WebServer*)0) { + __pmNoMem("main.wl_servers", + (wl_numServers + 1) * sizeof(WebServer), + PM_FATAL_ERR); + } + } + + /* Get server name */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract server name"); + skip_to_eol(configFile); + continue; + } + + if (wl_numServers) { + for (n = 0; n < wl_numServers; n++) { + if (strcmp(buf1, wl_serverInst[n].i_name) == 0) { + snprintf(emess, sizeof(emess), "duplicate server name (%s)", buf1); + yyerror(emess); + break; + } + } + if (n < wl_numServers) { + skip_to_eol(configFile); + continue; + } + } + + wl_serverInst[wl_numServers].i_name = strdup(buf1); + wl_serverInst[wl_numServers].i_inst = wl_numServers; + + server = &(wl_servers[wl_numServers]); + memset(server, 0, sizeof(*server)); + server->access.filePtr = -1; + server->error.filePtr = -1; + + /* Get server active flag */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract active flag"); + skip_to_eol(configFile); + continue; + } + + if (strcasecmp(buf1, "on") == 0) { + server->counts.active = 1; + } + else if (strcasecmp(buf1, "off") == 0) { + server->counts.active = 0; + } + else { + yyerror("illegal active flag"); + skip_to_eol(configFile); + continue; + } + + /* Get access log regex and file name */ + + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract access log regex"); + skip_to_eol(configFile); + continue; + } + + sts = getword(configFile, buf2, sizeof(buf2)); + + if (sts <= 0 || buf2[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract access log name"); + skip_to_eol(configFile); + continue; + } + + for (n = 0; n < wl_numRegex; n++) + if (strcmp(buf1, wl_regexTable[n].name) == 0) + break; + + if (n == wl_numRegex) { + snprintf(emess, sizeof(emess), "access log regex \"%s\" not defined", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } else if(wl_regexTable[n].c_statusPos > 0 && + wl_regexTable[n].s_statusPos > 0) { + /* common extended format or one that uses the same codes */ + server->counts.extendedp = 1; + if(strcmp(wl_regexTable[n].name, "SQUID") == 0) { + /* + * default squid format - uses text codes not numerics + * so it *has* to be a special case + */ + server->counts.extendedp = 2; + } + } + + server->access.format = n; + server->access.fileName = strdup(buf2); + + if (server->counts.active) { + tmpFp = fopen(server->access.fileName, "r"); + if (tmpFp == (FILE*)0) { + snprintf(emess, sizeof(emess), "cannot open access log \"%s\"", buf2); + yywarn(emess); + server->access.filePtr = -1; + } + else + fclose(tmpFp); + } + + /* Get error log regex and file name */ + + sts = getword(configFile, buf1, sizeof(buf1)); + + if (sts <= 0 || buf1[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract error log regex"); + skip_to_eol(configFile); + continue; + } + + sts = getword(configFile, buf2, sizeof(buf2)); + + if (sts <= 0 || buf2[0] == '\0') { + if (sts >= 0) + yyerror("unable to extract error log name"); + skip_to_eol(configFile); + continue; + } + + for (n = 0; n < wl_numRegex; n++) + if (strcmp(buf1, wl_regexTable[n].name) == 0) + break; + + if (n == wl_numRegex) { + snprintf(emess, sizeof(emess), "error log regex \"%s\" not defined", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } + + server->error.format = n; + server->error.fileName = strdup(buf2); + + if (server->counts.active) { + tmpFp = fopen(server->error.fileName, "r"); + if (tmpFp == (FILE*)0) { + snprintf(emess, sizeof(emess), "cannot open error log \"%s\"", buf2); + yywarn(emess); + server->error.filePtr = -1; + } + else + fclose(tmpFp); + } + + check_to_eol(configFile); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, "%d Server %s, %d, %d, %s, %d, %s\n", + wl_numServers, + wl_serverInst[wl_numServers].i_name, + server->counts.active, + server->access.format, + server->access.fileName, + server->error.format, + server->error.fileName); + } +#endif + + if (server->counts.active) + wl_numActive++; + + wl_numServers++; + } + else { + snprintf(emess, sizeof(emess), "illegal keyword \"%s\"", buf1); + yyerror(emess); + skip_to_eol(configFile); + continue; + } + } + + if (wl_numServers == 0) { + yyerror("no servers were specified in the configuration file!"); + } + + fclose(configFile); + + if (checkOnly || err) { + /* errors, or parse only, no PMCD communication option */ + exit(err); + } + + wl_indomTable[0].it_numinst = wl_numServers; + wl_indomTable[0].it_set = wl_serverInst; + + web_init(&desc); + pmdaConnect(&desc); + + /* catch any sprocs dying */ + + signal(SIGCHLD, onchld); + + /* fire off all the sprocs that we need */ + + wl_numSprocs = (wl_numServers-1) / wl_sprocThresh; + wl_sproc = (WebSproc*)malloc((wl_numSprocs+1) * sizeof(WebSproc)); + if (wl_sproc == NULL) { + logmessage(LOG_ERR, + "wl_numServers = %d, wl_sprocThresh = %d", + wl_numServers, + wl_sprocThresh); + __pmNoMem("main.wl_sproc", + (wl_numSprocs+1) * sizeof(WebSproc), + PM_FATAL_ERR); + } + + + for (n = 0; n <= wl_numSprocs; n++) + { + proc = &wl_sproc[n]; + proc->pid = -1; + proc->methodStr = (char *)0; + proc->sizeStr = (char *)0; + proc->c_statusStr = (char *)0; + proc->s_statusStr = (char *)0; + proc->strLength = 0; + } + + if (wl_numSprocs) { + + for (n=1; n<=wl_numSprocs; n++) { + proc = &wl_sproc[n]; + + sts = pipe1(proc->inFD); + if (sts) { + logmessage(LOG_ERR, + "Cannot allocate fileDes 1 for sproc[%d]", + n); + exit(1); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "Creating in pipe (in=%d, out=%d) for sproc %d\n", + proc->inFD[0], + proc->inFD[1], + n); +#endif + + sts = pipe1(proc->outFD); + if (sts) { + logmessage(LOG_ERR, + "Cannot allocate fileDes 2 for sproc[%d]", + n); + exit(1); + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "Creating out pipe (in=%d, out=%d) for sproc %d\n", + proc->outFD[0], + proc->outFD[1], + n); +#endif + + proc->firstServer = (n)*wl_sprocThresh; + if (n != wl_numSprocs) + proc->lastServer = proc->firstServer + + wl_sprocThresh - 1; + else + proc->lastServer = wl_numServers - 1; + + logmessage(LOG_INFO, + "Creating sproc [%d] for servers %d to %d\n", + n, proc->firstServer, proc->lastServer); + + proc->id = n; + +#ifndef HAVE_SPROC + proc->pid = sproc(sprocMain, CLONE_VM, (void*)(&proc->id)); +#else + proc->pid = sproc(sprocMain, PR_SADDR, (void*)(&proc->id)); +#endif + + if (proc->pid < 0) { + logmessage(LOG_ERR, "main: error creating sproc %d: %s\n", + n, osstrerror()); + exit(1); + } + +#ifdef PCP_DEBUG + if(pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_INFO, + "main: created sproc %d: pid %" FMT_PID "\n", + n, + proc->pid); + } +#endif + + /* close off unwanted pipes */ + + if(close(proc->inFD[0]) < 0) { + logmessage(LOG_WARNING, + "main: pipe close(fd=%d) failed: %s\n", + proc->inFD[0], osstrerror()); + } + if(close(proc->outFD[1]) < 0) { + logmessage(LOG_WARNING, + "main: pipe close(fd=%d) failed: %s\n", + proc->outFD[1], osstrerror()); + } + } + } + + wl_sproc[0].firstServer = 0; + wl_sproc[0].lastServer = (wl_numServers <= wl_sprocThresh) ? + wl_numServers - 1 : wl_sprocThresh - 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, + "Main process will monitor servers 0 to %d\n", + wl_sproc[0].lastServer); +#endif + + for (n=0; n <= wl_sproc[0].lastServer; n++) { + if (wl_servers[n].counts.active) { + openLogFile(&(wl_servers[n].access)); + openLogFile(&(wl_servers[n].error)); + } + } + +#ifdef PCP_DEBUG + __pmtimevalNow(&end); + startTime = (end.tv_sec - start.tv_sec) + + ((end.tv_usec - start.tv_usec) / 1000000.0); + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "Agent started in %f seconds", startTime); +#endif + + receivePDUs(&desc); + + logmessage(LOG_INFO, "Connection to PMCD closed by PMCD\n"); + logmessage(LOG_INFO, "Last fetch took %d msec\n", wl_catchupTime); + logmessage(LOG_INFO, "Exiting...\n"); + + return 0; +} diff --git a/src/pmdas/weblog/pmns b/src/pmdas/weblog/pmns new file mode 100644 index 0000000..fa2038d --- /dev/null +++ b/src/pmdas/weblog/pmns @@ -0,0 +1,306 @@ +/* + * Web Performance Metric Domain (PMD) Identifiers + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Cluster numbers are used by agent to indicate: + * + * 0 - Does not require any servers to be updated + * 1 - Requires all servers to be updated + * 2 - Requires only this server to be updated + * + * Changes to this file must also be carried into the help file and weblog.c + * (especially the meta table, web_fetch and web_store). + * + */ + +web { + config + allservers + perserver +} + +web.config { + numservers WEBSERVER:0:0 /* in the PMDA configuration file */ + catchup WEBSERVER:0:1 /* maximum catch-up period (secs) */ + catchuptime WEBSERVER:0:2 /* time spent in catchup (msecs) */ + check WEBSERVER:0:3 /* inactivity period (secs), before + trying to re-open files */ +} + +web.allservers { + numwatched WEBSERVER:0:4 /* number to be watched */ + numalive WEBSERVER:0:5 /* number of the watched servers + that are apparently alive */ + requests + bytes + errors WEBSERVER:1:6 +} + +web.allservers.requests { + total WEBSERVER:1:7 + get WEBSERVER:1:8 + head WEBSERVER:1:9 + post WEBSERVER:1:10 + other WEBSERVER:1:11 + size + client + cached + uncached +} + +web.allservers.bytes { + total WEBSERVER:1:12 + get WEBSERVER:1:13 + head WEBSERVER:1:14 + post WEBSERVER:1:15 + other WEBSERVER:1:16 + size + cached + uncached +} + +web.allservers.requests.size { + zero WEBSERVER:1:17 + le3k WEBSERVER:1:18 + le10k WEBSERVER:1:19 + le30k WEBSERVER:1:20 + le100k WEBSERVER:1:21 + le300k WEBSERVER:1:22 + le1m WEBSERVER:1:23 + le3m WEBSERVER:1:24 + gt3m WEBSERVER:1:25 + unknown WEBSERVER:1:66 +} + +web.allservers.bytes.size { + zero WEBSERVER:1:26 + le3k WEBSERVER:1:27 + le10k WEBSERVER:1:28 + le30k WEBSERVER:1:29 + le100k WEBSERVER:1:30 + le300k WEBSERVER:1:31 + le1m WEBSERVER:1:32 + le3m WEBSERVER:1:33 + gt3m WEBSERVER:1:34 +} + +web.allservers.requests.client { + total WEBSERVER:3:1 +} + +web.allservers.requests.cached { + total WEBSERVER:3:11 + size +} + +web.allservers.requests.cached.size { + zero WEBSERVER:3:12 + le3k WEBSERVER:3:13 + le10k WEBSERVER:3:14 + le30k WEBSERVER:3:15 + le100k WEBSERVER:3:16 + le300k WEBSERVER:3:17 + le1m WEBSERVER:3:18 + le3m WEBSERVER:3:19 + gt3m WEBSERVER:3:20 + unknown WEBSERVER:3:21 +} + +web.allservers.requests.uncached { + total WEBSERVER:3:31 + size +} + +web.allservers.requests.uncached.size { + zero WEBSERVER:3:32 + le3k WEBSERVER:3:33 + le10k WEBSERVER:3:34 + le30k WEBSERVER:3:35 + le100k WEBSERVER:3:36 + le300k WEBSERVER:3:37 + le1m WEBSERVER:3:38 + le3m WEBSERVER:3:39 + gt3m WEBSERVER:3:40 + unknown WEBSERVER:3:41 +} + +web.allservers.bytes.cached { + total WEBSERVER:3:51 + size +} + +web.allservers.bytes.cached.size { + zero WEBSERVER:3:52 + le3k WEBSERVER:3:53 + le10k WEBSERVER:3:54 + le30k WEBSERVER:3:55 + le100k WEBSERVER:3:56 + le300k WEBSERVER:3:57 + le1m WEBSERVER:3:58 + le3m WEBSERVER:3:59 + gt3m WEBSERVER:3:60 +} + +web.allservers.bytes.uncached { + total WEBSERVER:3:71 + size +} + +web.allservers.bytes.uncached.size { + zero WEBSERVER:3:72 + le3k WEBSERVER:3:73 + le10k WEBSERVER:3:74 + le30k WEBSERVER:3:75 + le100k WEBSERVER:3:76 + le300k WEBSERVER:3:77 + le1m WEBSERVER:3:78 + le3m WEBSERVER:3:79 + gt3m WEBSERVER:3:80 +} + +web.perserver { + watched WEBSERVER:0:35 /* is this server being watched? */ + numlogs WEBSERVER:2:36 /* number of logs I can read */ + requests + bytes + errors WEBSERVER:2:37 + logidletime WEBSERVER:2:68 +} + +web.perserver.requests { + total WEBSERVER:2:38 /* per server */ + get WEBSERVER:2:39 + head WEBSERVER:2:40 + post WEBSERVER:2:41 + other WEBSERVER:2:42 + size + client + cached + uncached +} + +web.perserver.bytes { + total WEBSERVER:2:43 /* per server */ + get WEBSERVER:2:44 + head WEBSERVER:2:45 + post WEBSERVER:2:46 + other WEBSERVER:2:47 + size + cached + uncached +} + +web.perserver.requests.size { + zero WEBSERVER:2:48 + le3k WEBSERVER:2:49 + le10k WEBSERVER:2:50 + le30k WEBSERVER:2:51 + le100k WEBSERVER:2:52 + le300k WEBSERVER:2:53 + le1m WEBSERVER:2:54 + le3m WEBSERVER:2:55 + gt3m WEBSERVER:2:56 + unknown WEBSERVER:2:67 +} + +web.perserver.bytes.size { + zero WEBSERVER:2:57 + le3k WEBSERVER:2:58 + le10k WEBSERVER:2:59 + le30k WEBSERVER:2:60 + le100k WEBSERVER:2:61 + le300k WEBSERVER:2:62 + le1m WEBSERVER:2:63 + le3m WEBSERVER:2:64 + gt3m WEBSERVER:2:65 +} + +web.perserver.requests.client { + total WEBSERVER:4:1 +} + +web.perserver.requests.cached { + total WEBSERVER:4:11 + size +} + +web.perserver.requests.cached.size { + zero WEBSERVER:4:12 + le3k WEBSERVER:4:13 + le10k WEBSERVER:4:14 + le30k WEBSERVER:4:15 + le100k WEBSERVER:4:16 + le300k WEBSERVER:4:17 + le1m WEBSERVER:4:18 + le3m WEBSERVER:4:19 + gt3m WEBSERVER:4:20 + unknown WEBSERVER:4:21 +} + +web.perserver.requests.uncached { + total WEBSERVER:4:31 + size +} + +web.perserver.requests.uncached.size { + zero WEBSERVER:4:32 + le3k WEBSERVER:4:33 + le10k WEBSERVER:4:34 + le30k WEBSERVER:4:35 + le100k WEBSERVER:4:36 + le300k WEBSERVER:4:37 + le1m WEBSERVER:4:38 + le3m WEBSERVER:4:39 + gt3m WEBSERVER:4:40 + unknown WEBSERVER:4:41 +} + +web.perserver.bytes.cached { + total WEBSERVER:4:51 + size +} + +web.perserver.bytes.cached.size { + zero WEBSERVER:4:52 + le3k WEBSERVER:4:53 + le10k WEBSERVER:4:54 + le30k WEBSERVER:4:55 + le100k WEBSERVER:4:56 + le300k WEBSERVER:4:57 + le1m WEBSERVER:4:58 + le3m WEBSERVER:4:59 + gt3m WEBSERVER:4:60 +} + +web.perserver.bytes.uncached { + total WEBSERVER:4:71 + size +} + +web.perserver.bytes.uncached.size { + zero WEBSERVER:4:72 + le3k WEBSERVER:4:73 + le10k WEBSERVER:4:74 + le30k WEBSERVER:4:75 + le100k WEBSERVER:4:76 + le300k WEBSERVER:4:77 + le1m WEBSERVER:4:78 + le3m WEBSERVER:4:79 + gt3m WEBSERVER:4:80 +} + diff --git a/src/pmdas/weblog/root b/src/pmdas/weblog/root new file mode 100644 index 0000000..de85ad7 --- /dev/null +++ b/src/pmdas/weblog/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { web } + +#include "pmns" + diff --git a/src/pmdas/weblog/server.sh b/src/pmdas/weblog/server.sh new file mode 100755 index 0000000..fc63dfa --- /dev/null +++ b/src/pmdas/weblog/server.sh @@ -0,0 +1,1228 @@ +#! /bin/sh +# +# Copyright (c) 2000-2001,2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Setup some default paths +_logdir=/var/log +_msgfil=messages + +# +# Every supported server defines 10 variables which are used by addServer() +# and installFiles() to create a server specification for the weblog PMDA +# and a URL for the webping PMDA, respectively. +# +# access The full path to the access log +# accessRegex The regex for the access log +# errors The full path to the error log +# errorRegex The regex for the error log +# serverPath The server path +# serverDesc A desciption of the type of server +# serverName The name for the server (must be unique) +# serverPort Port the server is bound to +# docs The full path to the document root +# "$noDocs" - indicates there is no document root +# http The URL for the server +# files How to put the HTML files into the doc root +# "link" - create a soft link +# "copy" - copy the files +# "skip" - do not create URLs for this server +# + +do_logs=false +do_files=false +do_auto=false +do_verbose=false +debug=false +noDocs="???" +unknownDocs="" +docsDir="$PCP_DOC_DIR/pcpweb/" +link="pcpweb" +file1="index.html" +file2="planning.html" +file3="tasks.html" +pfx=" " +skipping="${pfx}skipping..." +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +# Duplicate entry check file +# +rm -f $tmp/dup +touch $tmp/dup + +# pv 816562 - try to work around $PCP_DOC_DIR confusion +[ ! -x $docsDir -a -x /usr/pcp/doc/pcpweb/ ] && docsDir=/usr/pcp/doc/pcpweb/ + +LOCALHOSTNAME=`hostname` + +# web server attribute names (global) +# +ATTRLIST="serverPort serverName docs access errors" + +# look for netscape stuff here +# +NSROOTPATH="${NSROOTPATH-/usr/ns-home:/var/netscape/suitespot:/var/netscape/fasttrack}" + +# the Netscape server types we know how to handle +# +NSTYPE="httpd https proxy" + +# look for old outbox +# +OUTBOXPATH="${OUTBOXPATH-/var/mc-httpd}" + +# look for old Netscape Proxy Servers here +# +NSPROXYPATH="${NSPROXYPATH-/var/ns-proxy}" + +# look for NCSA servers here +# +NCSAPATH="${NCSAPATH-/var/www}" + +# look for Zeus servers here +# +ZEUSPATH="${ZEUSPATH-/usr/local/zeus}" + +# look for Squid Object caches here +# +SQUIDPATH="${SQUIDPATH-/usr/local/squid}" + +# look for Apache servers here +# +APACHEPATH="${APACHEPATH-/etc/apache2:/etc/httpd}" + +# look for anonymous ftp here +# +FTPPATH="${FTPPATH:-$_logdir}" + +# look for password file here (to determine ~ftp) +# +PASSWDPATH="${PASSWDPATH-/etc/passwd}" + +# look for SYSLOG here +# +SYSLOGPATH="${SYSLOGPATH:-$_logdir/$_msgfil}" + +# look for xferlog here (for wu_ftp) +# XFERLOG is default for wu_ftp +XFERLOG="${XFERLOG:-$_logdir/xferlog}" +# WUFTPLOG is used in sgi freeware wu_ftp +WUFTPLOG="${WUFTPLOG-$_logdir/wu-ftpd.log}" + +_getLogFiles() +{ + $PCP_ECHO_PROG $PCP_ECHO_N "Full path to access log [$access] ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + access="$ans" + if [ ! -f "$access" ] + then + echo "Warning: $access does not exist at this time." + fi + fi + + $PCP_ECHO_PROG $PCP_ECHO_N "Full path to error log [$errors] ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + errors="$ans" + if [ ! -f "$errors" ] + then + echo "Warning: $errors does not exist at this time." + fi + fi +} + +# _addServer_check +# 1: servername +# 2: other args for log file +# 3: server description +_addServer_check() +{ + if grep "$1" $tmp/dup > /dev/null 2>&1 + then + echo "${pfx}Error: already found a server with the name \"$1\"" + if $do_auto + then + echo "$skipping" + else + $PCP_ECHO_PROG $PCP_ECHO_N "New name for server (or return to skip this server): ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + if grep "$ans" $tmp/dup > /dev/null 2>&1 + then + echo "${pfx}Error: new server name already exists" + echo "$skipping" + else + echo >> $logFile + echo "# $3." >> $logFile + echo "server $ans $2" >> $logFile + echo "$1" >> $tmp/dup + fi + else + echo "$skipping" + fi + fi + else + echo >> $logFile + echo "# $3." >> $logFile + echo "server $1 $2" >> $logFile + echo "$1" >> $tmp/dup + fi +} + +_addServer() +{ + echo "Found $serverDesc at $serverPath" + if [ \( -f "$access" -o -c "$access" \) -a \( -f "$errors" -o -c "$errors" \) ] + then + found="true" + if [ -z "$serverPort" ] + then + echo "${pfx}identified as $serverName" + _addServer_check "$serverName" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + else + echo "${pfx}identified as $serverName:$serverPort" + _addServer_check "$serverName:$serverPort" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + fi + echo + else + echo "${pfx}Error: log files are not in the expected place:" + echo "${pfx} $access" + echo "${pfx} $errors" + if $do_auto + then + echo "$skipping" + else + echo + echo "Do you want to specify an alternate location for the log files" + $PCP_ECHO_PROG $PCP_ECHO_N "(otherwise this server will be ignored) [y] ""$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + _getLogFiles + if [ -z "$serverPort" ] + then + _addServer_check "$serverName" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + else + _addServer_check "$serverName:$serverPort" "on $accessRegex $access $errorRegex $errors" "$serverDesc" + fi + else + echo "$skipping" + fi + fi + echo + fi +} + +_installFiles() +{ + + echo + echo "Found $serverDesc at $serverPath" + if [ "$docs" != "$noDocs" ] + then + if [ "$docs" = "$unknownDocs" ] + then + echo "${pfx}Error: unable to determine document root" + if $do_auto + then + docs=$unknownDocs + echo "$skipping" + else + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Path to document root (return to skip HTML link for this server): ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + if [ -d "$ans" ] + then + docs="$ans" + else + echo "\"$ans\" is not a directory." + echo "No link to HTML files will be created for this server." + fi + fi + fi + elif [ ! -d "$docs" ] + then + echo "${pfx}Error: document root cannot be found at:" + echo "${pfx} $docs" + if $do_auto + then + docs=$unknownDocs + echo "$skipping" + else + echo + echo "Path to document root (return to skip HTML link for this server)" + $PCP_ECHO_PROG $PCP_ECHO_N "$docs: ""$PCP_ECHO_C" + read ans + if [ -n "$ans" ] + then + if [ -d $ans ] + then + docs=$ans + else + echo "${pfx}Error: \"$ans\" is not a directory." + docs=$unknownDocs + echo "$skipping" + fi + else + docs=$unknownDocs + fi + fi + else + echo "${pfx}document root found at:" + echo "${pfx}$docs" + fi + + if [ "$docs" != "$unknownDocs" ] + then + if [ ! -f $docs/$link/$file1 -o ! -f $docs/$link/$file2 -o ! -f $docs/$link/$file3 ] + then + if [ "$files" = "link" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want a link to some sample HTML files created in this directory [y] ""$PCP_ECHO_C" + elif [ "$files" = "copy" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want some sample HTML files installed in this directory [y] ""$PCP_ECHO_C" + fi + + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + if [ "$files" = "link" ] + then + [ -L $docs/$link ] && rm -f $docs/$link + if [ -f $docs/$link ] + then + echo "${pfx}Error: $docs/$link already exists." + echo "$skipping" + else + ln -s $docsDir $docs/$link + fi + elif [ "$files" = "copy" ] + then + if [ ! -f $docs/$link -o -d $docs/$link ] + then + cp $docsDir/$file1 $docs/$link/$file1 + cp $docsDir/$file2 $docs/$link/$file2 + cp $docsDir/$file3 $docs/$link/$file3 + else + echo "${pfx}Error: $docs/$link is not a directory." + echo "$skipping" + fi + fi + + if [ -z "$http" ] + then + echo "$http/$link/$file1" >> $logFile + echo "$http/$link/$file2" >> $logFile + echo "$http/$link/$file3" >> $logFile + fi + fi + else + echo "${pfx}Note: the link to the sample HTML files already exists." + if [ -n "$http" ] + then + echo "$http/$link/$file1" >> $logFile + echo "$http/$link/$file2" >> $logFile + echo "$http/$link/$file3" >> $logFile + fi + fi + else + if [ "$docs" != "$unknownDocs" ] + then + echo "$skipping" + fi + fi + else + echo "${pfx}Error: no document root, cannot install files." + echo "$skipping" + fi + echo +} + +_switchAction() +{ + if $do_verbose + then + echo "------------------------------------------------------------" + echo "access = $access" + echo "accessRegex = $accessRegex" + echo "errors = $errors" + echo "errorRegex = $errorRegex" + echo "serverPath = $serverPath" + echo "serverDesc = $serverDesc" + echo "serverName = $serverName" + echo "serverPort = $serverPort" + echo "docs = $docs" + echo "http = $http" + echo "files = $files" + echo "------------------------------------------------------------" + echo + fi + + if $do_logs + then + _addServer + else + _installFiles + fi +} + +_scan_config() +{ + serverPort= + serverName= + errors= + access= + docs= + + eval `cat $* 2>/dev/null | $PCP_AWK_PROG ' +NF == 2 && tolower($1) == "port" { print "serverPort=" $2; next } +NF == 2 && tolower($1) == "errorlog" { print "errors=" $2; next } +NF == 2 && tolower($1) == "servername" { print "serverName=" $2; next } +tolower($1) == "init" { for (i=1; i<=NF; i++) { + if (match($i, "^access=")) print $i + if (match($i, "^global=")) printf "access=%s ",substr($i,8,length($i)-7) + } + next + } +tolower($0) ~ /fn="document-root"/ || tolower($0) ~ /fn=document-root/ { + for (i=1; i<=NF; i++) { + if (match($i, "^root=")) + printf "docs=%s ",substr($i,6,length($i)-5) + } + next + }'` +} + +_netscape_extract() +{ + here=`pwd` + echo >$tmp/ns + for root in `echo "$NSROOTPATH" | sed -e 's/:/ /g'` + do + for type in $NSTYPE + do + for dir in $root/$type-* + do + [ -L "$dir" ] && continue + if [ -d "$dir" ] + then + cd $dir + check=`pwd` + cd $here + match=`grep "^$check " $tmp/ns` + if [ ! -z "$match" ] + then + echo "$match $dir" | $PCP_AWK_PROG ' + { if ($1 == $2) + printf "The server at %s appears to be a link to %s which is already monitored as %s\n", $3, $2, $1 + else if ($1 == $3) { + printf "The server at %s was already detected,\n", $1 + printf "using the link %s. ", $2 + } + else { + printf "The server at %s was already detected,\n", $1 + printf "using the link %s. The link %s,\n", $2, $3 + printf "which is also to this server, will be ignored.\n" + } + }' + echo "$skipping" + continue + fi + echo "$check $dir" >>$tmp/ns + access= + errors= + docs= + serverName= + serverPort= + + if [ ! -f $dir/config/obj.conf ] + then + echo "Found Netscape $type Server at $dir" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $dir/config/obj.conf" + echo "$skipping" + echo + continue + elif [ ! -f $dir/config/magnus.conf ] + then + echo "Found Netscape $type Server at $dir" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $dir/config/magnus.conf" + echo "$skipping" + echo + continue + fi + + _scan_config $dir/config/obj.conf $dir/config/magnus.conf + + # fix server name as Netscape often adds a trailing '.' + serverName=`echo $serverName | sed -e 's/\.$//'` + + if [ -z "$serverName" -o -z "$serverPort" ] + then + echo "Found Netscape $type Server at $dir" + echo "${pfx}Error: unable to determine server name or port from:" + echo "${pfx} $dir/config/magnus.conf" + echo "$skipping" + echo + continue + fi + + if [ -z "$access" ] + then + access=$dir/logs/access + echo "Found Netscape $type Server at $dir" + echo "${pfx}Warning: unable to determine access log name, assuming:" + echo "${pfx} $access" + echo + fi + + if [ -z "$errors" ] + then + errors=$dir/logs/errors + echo "Found Netscape $type Server at $dir" + echo "${pfx}Warning: unable to determine error log name, assuming:" + echo "${pfx} $errors" + echo + fi + + # + # figure out if this is a caching server or not + # + if [ -f $access ] + then + num_lines=`wc -l $access | $PCP_AWK_PROG '{print $1}'` + if [ $num_lines -gt 1 ] + then + num_fields=`tail -n 1 $access | cut -f3 -d\" | wc -w` + if [ $num_fields -eq 11 ] + then + accessRegex="NS_PROXY" + else + accessRegex="CERN" + fi + else + accessRegex="CERN" + fi + fi + + errorRegex="CERN_err" + serverPath="$dir" + serverDesc="Netscape $type Server" + http="GET http://$serverName:$serverPort" + files="link" + + _switchAction + fi + done + done + done +} + +_zeus_extract() +{ + if [ -d $ZEUSPATH ] + then + if [ -f $ZEUSPATH/server.ini ] + then + rm -f $tmp/zeus + touch $tmp/zeus + $PCP_AWK_PROG < $ZEUSPATH/server.ini -v hostname=$LOCALHOSTNAME -v out=$tmp/zeus -v ini=$ZEUSPATH/server.ini -F'=' ' +BEGIN { mode = 0; + port = 0; + name = ""; + access = "???"; + errors = "???"; + docs = "???"; + host = hostname; + } + +/^port/ { if (mode == 0) + port=$2; + next + } +$1 ~ /\[Admin/ { mode = 1; next } +$1 ~ /\[Server/ { if (mode == 2) { + printf("%s %s %s %d %s %s\n", name, access, errors, port, host, docs) > out; + name=""; access="???"; errors="???"; docs="???"; + host = hostname + } + else + mode = 2; + + i = match($1, " .*]"); + if (i == 0) { + mode = 1 + } + else { + name = substr($1, RSTART+1, RLENGTH-2); + } + next + } +/^logdir/ { if (mode == 2) { + access = sprintf("%s/transfer", $2); + errors = sprintf("%s/errors", $2); + } + next + } +/^docroot/ { if (mode == 2) + docs = $2; + next + } +/^ipname/ { if (mode == 2) + host = $2; + next + } +END { if (mode == 2) + printf("%s %s %s %d %s %s\n", name, access, errors, port, host, docs) > out; + }' + + if [ -f $tmp/zeus -a -s $tmp/zeus ] + then + accessRegex=CERN + errorRegex=CERN_err + serverPath=$ZEUSPATH + files="link" + lines=`wc -l $tmp/zeus | $PCP_AWK_PROG '{ print $1 }'` + count=1 + while [ $count -le $lines ] + do + eval `$PCP_AWK_PROG < $tmp/zeus -v line=$count ' +NR == line { printf("serverName=%s\naccess=%s\nerrors=%s\nserverPort=%d\nhttp=%s\ndocs=%s\n", $1, $2, $3, $4, $5, $6); exit }'` + + serverDesc="Zeus Server $serverName" + serverName="zeus-$serverName" + http="GET http://$http:$serverPort" + + if [ "$access" = "???" ] + then + access=$ZEUSPATH/log/transfer + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine access log name, assuming:" + echo "${pfx} $access" + echo + fi + if [ "$errors" = "???" ] + then + errors=$ZEUSPATH/log/errors + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine error log name, assuming:" + echo "${pfx} $errors" + echo + fi + if [ "$docs" = "???" ] + then + docs=$ZEUSPATH/docroot + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine document root, assuming:" + echo "${pfx} $docs" + echo + fi + + _switchAction + count=`expr $count + 1` + done + else + echo "Found Zeus Server/s at $ZEUSPATH" + echo "${pfx}Error: could not detect any servers in configuration file:" + echo "${pfx} $ZEUSPATH/server.ini" + echo "$skipping" + fi + + else + echo "Found Zeus Server at $ZEUSPATH" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $ZEUSPATH/server.ini" + echo "$skipping" + fi + fi +} + +_squid_extract() +{ + if [ -d $SQUIDPATH ] + then + if [ -f $SQUIDPATH/etc/squid.conf ] + then + rm -f $tmp/squid + touch $tmp/squid + $PCP_AWK_PROG < $SQUIDPATH/etc/squid.conf -v hostname=${LOCALHOSTNAME} -v out=$tmp/squid ' +BEGIN { port = 3128; + name = hostname; + access = "/usr/local/squid/logs/access.log"; + errors = "/dev/null"; + host = hostname; + } + +$1 == "emulate_httpd_log" { if ( $2 == "on" ) + mode = 1; + } +$1 == "cache_access_log" { access = $2; + } +$1 == "http_port" { port = $2; + } +$1 == "visible_hostname" { name = $2; + } +END { if (mode == 0) + printf("SQUID %s %s %s %d %s\n", name, access, errors, port, host) > out; + else + printf("CERN %s %s %s %d %s\n", name, access, errors, port, host) > out; + }' + + if [ -f $tmp/squid -a -s $tmp/squid ] + then + grep CERN $tmp/squid >/dev/null + if [ $? -eq 0 ] + then + accessRegex=CERN + else + accessRegex=SQUID + fi + errorRegex=CERN_err + serverPath=$SQUIDPATH + files="skip" + eval `$PCP_AWK_PROG < $tmp/squid ' +{ printf("serverName=%s\naccess=%s\nerrors=%s\nserverPort=%d\nhttp=%s\n", $2, $3, $4, $5, $6); exit }'` + + serverDesc="Squid Server $serverName" + serverName="squid-$serverName" + http="GET http://$http:$serverPort" + docs=$noDocs + + if [ "$access" = "???" ] + then + access=$SQUIDPATH/log/transfer + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine access log name, assuming:" + echo "${pfx} $access" + echo + fi + if [ "$errors" = "???" ] + then + errors=$SQUIDPATH/log/errors + echo "Found $serverDesc at $serverPath" + echo "${pfx}Warning: unable to determine error log name, assuming:" + echo "${pfx} $errors" + echo + fi + + _switchAction + else + echo "Found Squid Server/s at $SQUIDPATH" + echo "${pfx}Error: could not detect any servers in configuration file:" + echo "${pfx} $SQUIDPATH/squid.conf" + echo "$skipping" + fi + + else + echo "Found Squid Server at $SQUIDPATH" + echo "${pfx}Error: unable to find configuration file:" + echo "${pfx} $SQUIDPATH/squid.conf" + echo "$skipping" + fi + fi +} + +_apache_extract() +{ + for apchroot in `echo "$APACHEPATH" | sed -e 's/:/ /g'` + do + $debug && echo "_apache_extract: apachroot=$apchroot" + if [ -d "$apchroot/sites-available" ] + then + config="$apchroot/sites-available/" + elif [ -d "$apchroot/vhosts.d" ] + then + config="$apchroot/vhosts.d/" + elif [ -d "$apchroot/conf" ] + then + config="$apchroot/conf/" + elif [ -f "$apchroot/httpd.conf" ] + then + config="$apchroot/httpd.conf" + else + continue + fi + + for config in `echo ${config}*` + do + $debug && echo "_apache_extract: config=$config" + [ -f "$config" ] || continue + + cat $config \ + | sed -e's/#.*//' -e'/^$/d' \ + | $PCP_AWK_PROG -v def=`hostname` ' + BEGIN { + curnam=def; + names[def] = curnam; + ports[def] = 80; + } + $1 == "", "", $2); + n = split ($2, nm, ":"); + if ( n == 1 ) { + curnam=$2; + port=ports[def]; + } else { + if ( length (nm[1]) && nm[1] != "*" ) { + curnam = nm[1]; + } else { + curnam = def; + } + if ( length (nm[2]) ) { + port = nm[2]; + } else { + port = ports[def]; + } + } + cn=sprintf("%s:%d", curnam, port); + names[cn] = curnam; + ports[cn] = port; + curnam = cn; + } + $1 == "ServerName" { names[curnam] = $2; } + $1 == "Port" { ports[curnam] = $2; } + $1 == "" { curnam=def; } + $1 == "ErrorLog" { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + erlog[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + erlog[curnam] = path; + } + } + $1 == "DocumentRoot" { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + docs[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + docs[curnam] = path; + } + } + $1 == "TransferLog" { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + tlog[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + tlog[curnam] = path; + } + } + $1 == "CustomLog" && ($3 == "common" || $3 == "combined") { + path = $2 + sub (/\$\{APACHE_LOG_DIR\}/, "/var/log/apache2", path) + if ( match (path, "/") != 1 ) { + tlog[curnam] = sprintf ("%s/%s", "'$apchroot'", path); + } else { + tlog[curnam] = path; + } + } + END { + for ( n in names ) { + print names[n], ports[n], tlog[n], erlog[n], docs[n]; + } + }'\ + | while read serverName serverPort access errors docs ; do + $debug && echo "_apache_extract: serverName=$serverName serverPort=$serverPort access=$access errors=$errors docs=$docs" + + accessRegex=CERN + errorRegex=CERN_err + serverPath="$apchroot" + serverDesc="Apache Server" + files="copy" + + _switchAction + done + done + done +} + +while [ $# -gt 0 ] +do + case $1 + in + + -d) # debug + debug=true + ;; + + -f) # install dummy HTML files in server document root + do_files=true + if [ $# -gt 1 ] + then + shift + logFile=$1 + else + echo "-f requires the name of log file" + exit 1 + fi + ;; + + -l) # generate pmdaweblog configuration file + do_logs=true + if [ $# -gt 1 ] + then + shift + logFile=$1 + else + echo "-l requires the name of log file" + exit 1 + fi + ;; + + -q) # do not prompt for misconfigured servers + do_auto=true + ;; + + -v) # verbose + do_verbose=true + ;; + + *) # USAGE + echo "Usage: server.sh [-dqv] [-f logFile] [-l logFile]" + exit 1 + ;; + esac + shift +done + +if [ "$do_logs" = "true" -a "$do_files" = "true" ] +then + echo "May only perform one of the two options at any one time" + exit 1 +fi + +if $do_files +then + if [ ! -f $docsDir/$file1 -o ! -f $docsDir/$file2 -o ! -f $docsDir/$file3 ] + then + echo "Some or all of the sample HTML files ($file1, $file2, $file3)" + echo "are missing. Cannot continue!" + exit 1 + fi +fi + +_netscape_extract + +# Another common place for Netscape servers + +if [ -d $OUTBOXPATH ] +then + access=$OUTBOXPATH/logs/access + accessRegex="CERN" + errors=$OUTBOXPATH/logs/errors + errorRegex="CERN_err" + serverPath=$OUTBOXPATH + serverDesc="Outbox Server" + serverName="outbox-$LOCALHOSTNAME" + serverPort= + docs="$OUTBOXPATH/html" + http="GET http://$LOCALHOSTNAME" + files="link" + _switchAction +fi + +# Netscape Proxy Server + +if [ -d $NSPROXYPATH/logs ] +then + access=$NSPROXYPATH/logs/access + accessRegex="CERN" + errors=$NSPROXYPATH/logs/errors + errorRegex="CERN_err" + serverPath=$NSPROXYPATH + serverDesc="Old Netscape proxy Server" + serverName="proxy-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction +fi + +# Netscape SOCKS Proxy Server + +if [ -f $NSROOTPATH/proxy-server/logs/sockd ] +then + access=$NSROOTPATH/proxy-server/logs/sockd + accessRegex="NS_SOCKS" + errors=/dev/null + errorRegex="NS_SOCKS_err" + serverPath=$NSROOTPATH/proxy-server + serverDesc="Netscape SOCKS Proxy Server" + serverName="socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + + if [ "$do_logs" = "true" ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like to log SOCKS ftp transactions [y] ""$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + access=$NSROOTPATH/proxy-server/logs/sockd + accessRegex="NS_FTP" + errors=/dev/null + errorRegex="NS_FTP_err" + serverPath=$NSROOTPATH/proxy-server + serverDesc="FTP through Netscape SOCKS Server" + serverName="ftp-socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + fi + fi +fi + +if [ -f $NSPROXYPATH/logs/sockd ] +then + access=$NSPROXYPATH/logs/sockd + accessRegex="NS_SOCKS" + errors=/dev/null + errorRegex="NS_SOCKS_err" + serverPath=$NSPROXYPATH + serverDesc="Netscape SOCKS Proxy Server" + serverName="socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + + if [ "$do_logs" = "true" ] + then + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like to log SOCKS ftp transactions [y] ""$PCP_ECHO_C" + read ans + if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] + then + access=$NSPROXYPATH/logs/sockd + accessRegex="NS_FTP" + errors=/dev/null + errorRegex="NS_FTP_err" + serverPath=$NSPROXYPATH + serverDesc="FTP through Netscape SOCKS Server" + serverName="ftp-socks-$LOCALHOSTNAME" + serverPort= + docs=$noDocs + http="" + files="skip" + _switchAction + fi + fi +fi + +# NCSA (or derived) Server + +if [ -d $NCSAPATH/server ] +then + access=$NCSAPATH/server/logs/access_log + accessRegex="CERN" + errors=$NCSAPATH/server/logs/error_log + errorRegex="CERN_err" + serverPath=$NCSAPATH/server + serverDesc="NCSA (or derived) Server" + serverName="ncsa-$LOCALHOSTNAME" + serverPort= + docs="$NCSAPATH/htdocs" + http="GET http://$LOCALHOSTNAME" + files="link" + _switchAction +fi + +# Zeus Server + +_zeus_extract + +# Squid Server + +_squid_extract + +# Apache Server + +_apache_extract + +# Oracle Webserver + +if [ -n "$ORACLE_HOME" ] +then + if [ -d $ORACLE_HOME/ows ] + then + serverPath=$ORACLE_HOME/ows + for i in `ls $serverPath/log/sv*.log*` + do + serverPort=`basename $i | sed 's/sv//' | sed 's/\.log//'` + access=$i + accessRegex="CERN" + errors="$serverPath/ows/log/sv$serverPort.err" + errorRegex="CERN_err" + serverDesc="Oracle Webserver" + serverName="ows-$serverPort" + docs="$serverPath/doc" + http="GET http://${LOCALHOSTNAME}:$serverPort" + files="link" + _switchAction + done + fi +fi + +# Harvest Cache + +if [ -n "$HARVEST_HOME" ] +then + serverPath=$HARVEST_HOME + serverPort=80 + access=$serverPath/cache.access.log + accessRegex="CERN" + errors="$serverPath/cache.log" + errorRegex="CERN_err" + serverDesc="Harvest Cache" + serverName="harvest-cache" + docs="$noDocs" + http="" + files="skip" + _switchAction +elif [ -d /usr/local/harvest ] +then + serverPath=/usr/local/harvest + serverPort=80 + access=$serverPath/cache.access.log + accessRegex="CERN" + errors="$serverPath/cache.log" + errorRegex="CERN_err" + serverDesc="Harvest Cache" + serverName="harvest-cache" + docs="$noDocs" + http="" + files="skip" + _switchAction +fi + +# FTP +if [ -n "$QUIET_INSTALL" ]; then + ans=y +else + echo + if [ "$do_logs" = "true" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N "Do you also want to monitor ftp transactions [y] ""$PCP_ECHO_C" + else + $PCP_ECHO_PROG $PCP_ECHO_N "Would you like the sample HTML files installed for anonymous ftp [y] ""$PCP_ECHO_C" + fi + read ans +fi + +if [ -z "$ans" -o "$ans" = "y" -o "$ans" = "Y" ] +then + echo + if [ -f $PASSWDPATH ] + then + if [ -n "$QUIET_INSTALL" ] ; then + ans=y + else + $PCP_ECHO_PROG $PCP_ECHO_N "Do you want to monitor wu_ftp [n] ""$PCP_ECHO_C" + read ans + echo + fi + if [ "$ans" = "y" -o "$ans" = "Y" ] + then + wulog="$WUFTPLOG" + if [ -f "$WUFTPLOG" ] + then + wulog="$WUFTPLOG" + elif [ -f "$XFERLOG" ] + then + wulog="$XFERLOG" + else + if $do_auto + then + echo "${pfx}Error: log files are not in the expected place:" + echo "$pfx $XFERLOG" + echo "$pfx or $WUFTPLOG" + echo "$skipping" + wulog="" + fi + fi + + access="$wulog" + accessRegex="WU_FTP" + errors="$SYSLOGPATH" + errorRegex="WU_FTP_err" + serverDesc="WU_FTP Server" + serverName="wu_ftp" + serverPath=$FTPPATH + serverPort= + else + access=$SYSLOGPATH + accessRegex="SYSLOG_FTP" + errors=$SYSLOGPATH + errorRegex="SYSLOG_FTP_err" + serverDesc="FTP Server" + serverName="ftpd" + serverPath=$FTPPATH + serverPort= + fi + + if [ ! -z "$access" ] + then + docs="" + docs=`$PCP_AWK_PROG -F: '$1 == "ftp" { print $6 "/pub"; exit }' < ${PASSWDPATH}` + if [ -z "$docs" ] + then + echo "Found FTP Server at $FTPPATH" + echo "${pfx}Error: user ftp is not listed in the password file:" + echo "${pfx} $PASSWDPATH" + echo "$skipping" + else + http="GET ftp://$LOCALHOSTNAME/pub" + files="copy" + _switchAction + fi + fi + else + echo "Found FTP Server at $FTPPATH" + echo "${pfx}Error: unable to find password file:" + echo "${pfx} $PASSWDPATH" + echo "$skipping" + fi +fi + +if $do_logs +then + : +else + [ -f "$logFile" ] && sort -u $logFile -o $logFile +fi diff --git a/src/pmdas/weblog/sproc.c b/src/pmdas/weblog/sproc.c new file mode 100644 index 0000000..e9fdc7d --- /dev/null +++ b/src/pmdas/weblog/sproc.c @@ -0,0 +1,39 @@ +/* + * Web PMDA, based on generic driver for a daemon-based PMDA + * + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "weblog.h" +#if defined(HAVE_PTHREAD_H) +#include +#endif +#if defined(HAVE_SCHED_H) +#include +#endif + +#if defined(HAVE_PTHREAD_H) +static pthread_t sproc_thread; + +int sproc (void (*entry) (void *), int flags, void *arg) +{ + int retval; + + retval = pthread_create(&sproc_thread, NULL, (void (*))entry, NULL); + return retval; +} +#endif diff --git a/src/pmdas/weblog/weblog.c b/src/pmdas/weblog/weblog.c new file mode 100644 index 0000000..570d104 --- /dev/null +++ b/src/pmdas/weblog/weblog.c @@ -0,0 +1,3132 @@ +/* + * Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "weblog.h" +#include "domain.h" +#include +#include +#if defined(HAVE_SYS_RESOURCE_H) +#include +#endif +#if defined(HAVE_SYS_PRCTL_H) +#include +#endif +#if defined(HAVE_SYS_WAIT_H) +#include +#endif + +/* + * Types of metrics, used by fetch to more efficiently calculate metrics + */ + +enum MetaType { + wl_globalPtr, wl_offset32, wl_offset64, wl_totalAggregate, + wl_serverAggregate, wl_requestMethod, wl_bytesMethod, + wl_requestSize, wl_requestCachedSize, wl_requestUncachedSize, + wl_bytesSize, wl_bytesCachedSize, wl_bytesUncachedSize, + wl_watched, wl_numAlive, wl_nosupport, wl_numMetaTypes +}; + +/* + * Return code of checkLogFile() + */ + +enum LogFileCode { + wl_ok, wl_opened, wl_reopened, wl_closed, wl_unableToOpen, wl_unableToStat, + wl_irregularFile, wl_dormant +}; + +static WebCount dummyCount; + +/* + * Instance domain table + * This is completed when parsing the config file + */ + +pmdaIndom wl_indomTable[] = +{ +#define WEBLOG_INDOM 0 + { WEBLOG_INDOM, 0, 0 } +}; + +/* + * Metric specific data to help identify each metric during a fetch + * + * MG: Note: must be in the same order as wl_metric table below. + * + */ + +typedef struct { + int m_type; + __psint_t m_offset; +} WebMetric; + +WebMetric wl_metricInfo[] = +{ +/* config.numservers */ + { wl_globalPtr, (__psint_t)&wl_numServers }, +/* config.catchup */ + { wl_globalPtr, (__psint_t)&wl_refreshDelay }, +/* config.catchuptime */ + { wl_globalPtr, (__psint_t)&wl_catchupTime }, +/* config.check */ + { wl_globalPtr, (__psint_t)&wl_chkDelay }, +/* allserves.numwatched */ + { wl_globalPtr, (__psint_t)&wl_numActive }, +/* allserves.numalive */ + { wl_numAlive, (__psint_t)0 }, +/* allservers.errors */ + { wl_totalAggregate, (__psint_t)0 }, +/* allservers.requests.total */ + { wl_totalAggregate, (__psint_t)1 }, +/* allservers.requests.get */ + { wl_requestMethod, (__psint_t)wl_httpGet }, +/* allservers.requests.head */ + { wl_requestMethod, (__psint_t)wl_httpHead }, +/* allservers.requests.post */ + { wl_requestMethod, (__psint_t)wl_httpPost }, +/* allservers.requests.other */ + { wl_requestMethod, (__psint_t)wl_httpOther }, +/* allservers.bytes.total */ + { wl_totalAggregate, (__psint_t)2 }, +/* allservers.bytes.get */ + { wl_bytesMethod, (__psint_t)wl_httpGet }, +/* allservers.bytes.head */ + { wl_bytesMethod, (__psint_t)wl_httpHead }, +/* allservers.bytes.post */ + { wl_bytesMethod, (__psint_t)wl_httpPost }, +/* allservers.bytes.other */ + { wl_bytesMethod, (__psint_t)wl_httpOther }, +/* allservers.requests.size.zero */ + { wl_requestSize, (__psint_t)wl_zero }, +/* allservers.requests.size.le3k */ + { wl_requestSize, (__psint_t)wl_le3k }, +/* allservers.requests.size.le10k */ + { wl_requestSize, (__psint_t)wl_le10k }, +/* allservers.requests.size.le30k */ + { wl_requestSize, (__psint_t)wl_le30k }, +/* allservers.requests.size.le100k */ + { wl_requestSize, (__psint_t)wl_le100k }, +/* allservers.requests.size.le300k */ + { wl_requestSize, (__psint_t)wl_le300k }, +/* allservers.requests.size.le1m */ + { wl_requestSize, (__psint_t)wl_le1m }, +/* allservers.requests.size.le3m */ + { wl_requestSize, (__psint_t)wl_le3m }, +/* allservers.requests.size.gt3m */ + { wl_requestSize, (__psint_t)wl_gt3m }, +/* allservers.requests.size.unknown */ + { wl_requestSize, (__psint_t)wl_unknownSize }, +/* allservers.requests.client.total */ + { wl_totalAggregate, (__psint_t)3 }, +/* allservers.requests.cached.total */ + { wl_totalAggregate, (__psint_t)4 }, +/* allservers.requests.cached.size.zero */ + { wl_requestCachedSize, (__psint_t)wl_zero }, +/* allservers.requests.cached.size.le3k */ + { wl_requestCachedSize, (__psint_t)wl_le3k }, +/* allservers.requests.cached.size.le10k */ + { wl_requestCachedSize, (__psint_t)wl_le10k }, +/* allservers.requests.cached.size.le30k */ + { wl_requestCachedSize, (__psint_t)wl_le30k }, +/* allservers.requests.cached.size.le100k */ + { wl_requestCachedSize, (__psint_t)wl_le100k }, +/* allservers.requests.cached.size.le300k */ + { wl_requestCachedSize, (__psint_t)wl_le300k }, +/* allservers.requests.cached.size.le1m */ + { wl_requestCachedSize, (__psint_t)wl_le1m }, +/* allservers.requests.cached.size.le3m */ + { wl_requestCachedSize, (__psint_t)wl_le3m }, +/* allservers.requests.cached.size.gt3m */ + { wl_requestCachedSize, (__psint_t)wl_gt3m }, +/* allservers.requests.cached.size.unknown */ + { wl_requestCachedSize, (__psint_t)wl_unknownSize }, +/* allservers.requests.uncached.total */ + { wl_totalAggregate, (__psint_t)5 }, +/* allservers.requests.uncached.size.zero */ + { wl_requestUncachedSize, (__psint_t)wl_zero }, +/* allservers.requests.uncached.size.le3k */ + { wl_requestUncachedSize, (__psint_t)wl_le3k }, +/* allservers.requests.uncached.size.le10k */ + { wl_requestUncachedSize, (__psint_t)wl_le10k }, +/* allservers.requests.uncached.size.le30k */ + { wl_requestUncachedSize, (__psint_t)wl_le30k }, +/* allservers.requests.uncached.size.le100k */ + { wl_requestUncachedSize, (__psint_t)wl_le100k }, +/* allservers.requests.uncached.size.le300k */ + { wl_requestUncachedSize, (__psint_t)wl_le300k }, +/* allservers.requests.uncached.size.le1m */ + { wl_requestUncachedSize, (__psint_t)wl_le1m }, +/* allservers.requests.uncached.size.le3m */ + { wl_requestUncachedSize, (__psint_t)wl_le3m }, +/* allservers.requests.uncached.size.gt3m */ + { wl_requestUncachedSize, (__psint_t)wl_gt3m }, +/* allservers.requests.uncached.size.unknown */ + { wl_requestUncachedSize, (__psint_t)wl_unknownSize }, +/* allservers.bytes.size.zero */ + { wl_bytesSize, (__psint_t)wl_zero }, +/* allservers.bytes.size.le3k */ + { wl_bytesSize, (__psint_t)wl_le3k }, +/* allservers.bytes.size.le10k */ + { wl_bytesSize, (__psint_t)wl_le10k }, +/* allservers.bytes.size.le30k */ + { wl_bytesSize, (__psint_t)wl_le30k }, +/* allservers.bytes.size.le100k */ + { wl_bytesSize, (__psint_t)wl_le100k }, +/* allservers.bytes.size.le300k */ + { wl_bytesSize, (__psint_t)wl_le300k }, +/* allservers.bytes.size.le1m */ + { wl_bytesSize, (__psint_t)wl_le1m }, +/* allservers.bytes.size.le3m */ + { wl_bytesSize, (__psint_t)wl_le3m }, +/* allservers.bytes.size.gt3m */ + { wl_bytesSize, (__psint_t)wl_gt3m }, +/* allservers.bytes.cached.total */ + { wl_totalAggregate, (__psint_t)6 }, +/* allservers.bytes.cached.size.zero */ + { wl_bytesCachedSize, (__psint_t)wl_zero }, +/* allservers.bytes.cached.size.le3k */ + { wl_bytesCachedSize, (__psint_t)wl_le3k }, +/* allservers.bytes.cached.size.le10k */ + { wl_bytesCachedSize, (__psint_t)wl_le10k }, +/* allservers.bytes.cached.size.le30k */ + { wl_bytesCachedSize, (__psint_t)wl_le30k }, +/* allservers.bytes.cached.size.le100k */ + { wl_bytesCachedSize, (__psint_t)wl_le100k }, +/* allservers.bytes.cached.size.le300k */ + { wl_bytesCachedSize, (__psint_t)wl_le300k }, +/* allservers.bytes.cached.size.le1m */ + { wl_bytesCachedSize, (__psint_t)wl_le1m }, +/* allservers.bytes.cached.size.le3m */ + { wl_bytesCachedSize, (__psint_t)wl_le3m }, +/* allservers.bytes.cached.size.gt3m */ + { wl_bytesCachedSize, (__psint_t)wl_gt3m }, +/* allservers.bytes.uncached.total */ + { wl_totalAggregate, (__psint_t)7 }, +/* allservers.bytes.uncached.size.zero */ + { wl_bytesUncachedSize, (__psint_t)wl_zero }, +/* allservers.bytes.uncached.size.le3k */ + { wl_bytesUncachedSize, (__psint_t)wl_le3k }, +/* allservers.bytes.uncached.size.le10k */ + { wl_bytesUncachedSize, (__psint_t)wl_le10k }, +/* allservers.bytes.uncached.size.le30k */ + { wl_bytesUncachedSize, (__psint_t)wl_le30k }, +/* allservers.bytes.uncached.size.le100k */ + { wl_bytesUncachedSize, (__psint_t)wl_le100k }, +/* allservers.bytes.uncached.size.le300k */ + { wl_bytesUncachedSize, (__psint_t)wl_le300k }, +/* allservers.bytes.uncached.size.le1m */ + { wl_bytesUncachedSize, (__psint_t)wl_le1m }, +/* allservers.bytes.uncached.size.le3m */ + { wl_bytesUncachedSize, (__psint_t)wl_le3m }, +/* allservers.bytes.uncached.size.gt3m */ + { wl_bytesUncachedSize, (__psint_t)wl_gt3m }, +/* perserver.watched */ + { wl_watched, (__psint_t)&dummyCount.active }, +/* perserver.numlogs */ + { wl_offset32, (__psint_t)&dummyCount.numLogs }, +/* perserver.errors */ + { wl_offset32, (__psint_t)&dummyCount.errors }, +/* perserver.requests.total */ + { wl_serverAggregate, (__psint_t)0 }, +/* perserver.requests.get */ + { wl_requestMethod, (__psint_t)wl_httpGet }, +/* perserver.requests.head */ + { wl_requestMethod, (__psint_t)wl_httpHead }, +/* perserver.requests.post */ + { wl_requestMethod, (__psint_t)wl_httpPost }, +/* perserver.requests.other */ + { wl_requestMethod, (__psint_t)wl_httpOther }, +/* perserver.bytes.total */ + { wl_serverAggregate, (__psint_t)1 }, +/* perserver.bytes.get */ + { wl_bytesMethod, (__psint_t)wl_httpGet }, +/* perserver.bytes.head */ + { wl_bytesMethod, (__psint_t)wl_httpHead }, +/* perserver.bytes.post */ + { wl_bytesMethod, (__psint_t)wl_httpPost }, +/* perserver.bytes.other */ + { wl_bytesMethod, (__psint_t)wl_httpOther }, +/* perserver.requests.size.zero */ + { wl_requestSize, (__psint_t)wl_zero }, +/* perserver.requests.size.le3k */ + { wl_requestSize, (__psint_t)wl_le3k }, +/* perserver.requests.size.le10k */ + { wl_requestSize, (__psint_t)wl_le10k }, +/* perserver.requests.size.le30k */ + { wl_requestSize, (__psint_t)wl_le30k }, +/* perserver.requests.size.le100k */ + { wl_requestSize, (__psint_t)wl_le100k }, +/* perserver.requests.size.le300k */ + { wl_requestSize, (__psint_t)wl_le300k }, +/* perserver.requests.size.le1m */ + { wl_requestSize, (__psint_t)wl_le1m }, +/* perserver.requests.size.le3m */ + { wl_requestSize, (__psint_t)wl_le3m }, +/* perserver.requests.size.gt3m */ + { wl_requestSize, (__psint_t)wl_gt3m }, +/* perserver.requests.size.unknown */ + { wl_requestSize, (__psint_t)wl_unknownSize }, +/* perserver.requests.client.total */ + { wl_serverAggregate, (__psint_t)2 }, +/* perserver.requests.cached.total */ + { wl_serverAggregate, (__psint_t)3 }, +/* perserver.requests.cached.size.zero */ + { wl_requestCachedSize, (__psint_t)wl_zero }, +/* perserver.requests.cached.size.le3k */ + { wl_requestCachedSize, (__psint_t)wl_le3k }, +/* perserver.requests.cached.size.le10k */ + { wl_requestCachedSize, (__psint_t)wl_le10k }, +/* perserver.requests.cached.size.le30k */ + { wl_requestCachedSize, (__psint_t)wl_le30k }, +/* perserver.requests.cached.size.le100k */ + { wl_requestCachedSize, (__psint_t)wl_le100k }, +/* perserver.requests.cached.size.le300k */ + { wl_requestCachedSize, (__psint_t)wl_le300k }, +/* perserver.requests.cached.size.le1m */ + { wl_requestCachedSize, (__psint_t)wl_le1m }, +/* perserver.requests.cached.size.le3m */ + { wl_requestCachedSize, (__psint_t)wl_le3m }, +/* perserver.requests.cached.size.gt3m */ + { wl_requestCachedSize, (__psint_t)wl_gt3m }, +/* perserver.requests.cached.size.unknown */ + { wl_requestCachedSize, (__psint_t)wl_unknownSize }, +/* perserver.requests.uncached.total */ + { wl_serverAggregate, (__psint_t)4 }, +/* perserver.requests.uncached.size.zero */ + { wl_requestUncachedSize, (__psint_t)wl_zero }, +/* perserver.requests.uncached.size.le3k */ + { wl_requestUncachedSize, (__psint_t)wl_le3k }, +/* perserver.requests.uncached.size.le10k */ + { wl_requestUncachedSize, (__psint_t)wl_le10k }, +/* perserver.requests.uncached.size.le30k */ + { wl_requestUncachedSize, (__psint_t)wl_le30k }, +/* perserver.requests.uncached.size.le100k */ + { wl_requestUncachedSize, (__psint_t)wl_le100k }, +/* perserver.requests.uncached.size.le300k */ + { wl_requestUncachedSize, (__psint_t)wl_le300k }, +/* perserver.requests.uncached.size.le1m */ + { wl_requestUncachedSize, (__psint_t)wl_le1m }, +/* perserver.requests.uncached.size.le3m */ + { wl_requestUncachedSize, (__psint_t)wl_le3m }, +/* perserver.requests.uncached.size.gt3m */ + { wl_requestUncachedSize, (__psint_t)wl_gt3m }, +/* perserver.requests.uncached.size.unknown */ + { wl_requestUncachedSize, (__psint_t)wl_unknownSize }, +/* perserver.bytes.size.zero */ + { wl_bytesSize, (__psint_t)wl_zero }, +/* perserver.bytes.size.le3k */ + { wl_bytesSize, (__psint_t)wl_le3k }, +/* perserver.bytes.size.le10k */ + { wl_bytesSize, (__psint_t)wl_le10k }, +/* perserver.bytes.size.le30k */ + { wl_bytesSize, (__psint_t)wl_le30k }, +/* perserver.bytes.size.le100k */ + { wl_bytesSize, (__psint_t)wl_le100k }, +/* perserver.bytes.size.le300k */ + { wl_bytesSize, (__psint_t)wl_le300k }, +/* perserver.bytes.size.le1m */ + { wl_bytesSize, (__psint_t)wl_le1m }, +/* perserver.bytes.size.le3m */ + { wl_bytesSize, (__psint_t)wl_le3m }, +/* perserver.bytes.size.gt3m */ + { wl_bytesSize, (__psint_t)wl_gt3m }, +/* perserver.bytes.cached.total */ + { wl_serverAggregate, (__psint_t)5 }, +/* perserver.bytes.cached.size.zero */ + { wl_bytesCachedSize, (__psint_t)wl_zero }, +/* perserver.bytes.cached.size.le3k */ + { wl_bytesCachedSize, (__psint_t)wl_le3k }, +/* perserver.bytes.cached.size.le10k */ + { wl_bytesCachedSize, (__psint_t)wl_le10k }, +/* perserver.bytes.cached.size.le30k */ + { wl_bytesCachedSize, (__psint_t)wl_le30k }, +/* perserver.bytes.cached.size.le100k */ + { wl_bytesCachedSize, (__psint_t)wl_le100k }, +/* perserver.bytes.cached.size.le300k */ + { wl_bytesCachedSize, (__psint_t)wl_le300k }, +/* perserver.bytes.cached.size.le1m */ + { wl_bytesCachedSize, (__psint_t)wl_le1m }, +/* perserver.bytes.cached.size.le3m */ + { wl_bytesCachedSize, (__psint_t)wl_le3m }, +/* perserver.bytes.cached.size.gt3m */ + { wl_bytesCachedSize, (__psint_t)wl_gt3m }, +/* perserver.bytes.uncached.total */ + { wl_serverAggregate, (__psint_t)6 }, +/* perserver.bytes.uncached.size.zero */ + { wl_bytesUncachedSize, (__psint_t)wl_zero }, +/* perserver.bytes.uncached.size.le3k */ + { wl_bytesUncachedSize, (__psint_t)wl_le3k }, +/* perserver.bytes.uncached.size.le10k */ + { wl_bytesUncachedSize, (__psint_t)wl_le10k }, +/* perserver.bytes.uncached.size.le30k */ + { wl_bytesUncachedSize, (__psint_t)wl_le30k }, +/* perserver.bytes.uncached.size.le100k */ + { wl_bytesUncachedSize, (__psint_t)wl_le100k }, +/* perserver.bytes.uncached.size.le300k */ + { wl_bytesUncachedSize, (__psint_t)wl_le300k }, +/* perserver.bytes.uncached.size.le1m */ + { wl_bytesUncachedSize, (__psint_t)wl_le1m }, +/* perserver.bytes.uncached.size.le3m */ + { wl_bytesUncachedSize, (__psint_t)wl_le3m }, +/* perserver.bytes.uncached.size.gt3m */ + { wl_bytesUncachedSize, (__psint_t)wl_gt3m }, +/* perserver.logidletime */ + { wl_offset32, (__psint_t)&dummyCount.modTime }, +}; + +/* + * all metrics supported in this PMDA - one table entry for each + */ + +static pmdaMetric wl_metrics[] = { + +/* + * web.config + */ + +/* config.numservers */ +{ (void *)0, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, 0) } }, + +/* config.catchup */ +{ (void *)0, + { PMDA_PMID(0,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +/* config.catchuptime */ +{ (void *)0, + { PMDA_PMID(0,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +/* config.check */ +{ (void *)0, + { PMDA_PMID(0,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +/* + * web.allservers + */ + +/* allserves.numwatched */ +{ (void *)0, + { PMDA_PMID(0,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, 0) } }, + +/* allserves.numalive */ +{ (void *)0, + { PMDA_PMID(0,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, 0) } }, + +/* allservers.errors */ +{ (void *)0, + { PMDA_PMID(1,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.total */ +{ (void *)0, + { PMDA_PMID(1,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.get */ +{ (void *)0, + { PMDA_PMID(1,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.head */ +{ (void *)0, + { PMDA_PMID(1,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.post */ +{ (void *)0, + { PMDA_PMID(1,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.other */ +{ (void *)0, + { PMDA_PMID(1,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.bytes.total */ +{ (void *)0, + { PMDA_PMID(1,12), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.get */ +{ (void *)0, + { PMDA_PMID(1,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.head */ +{ (void *)0, + { PMDA_PMID(1,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.post */ +{ (void *)0, + { PMDA_PMID(1,15), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.other */ +{ (void *)0, + { PMDA_PMID(1,16), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.requests.size.zero */ +{ (void *)0, + { PMDA_PMID(1,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le3k */ +{ (void *)0, + { PMDA_PMID(1,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le10k */ +{ (void *)0, + { PMDA_PMID(1,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le30k */ +{ (void *)0, + { PMDA_PMID(1,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le100k */ +{ (void *)0, + { PMDA_PMID(1,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le300k */ +{ (void *)0, + { PMDA_PMID(1,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le1m */ +{ (void *)0, + { PMDA_PMID(1,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.le3m */ +{ (void *)0, + { PMDA_PMID(1,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.gt3m */ +{ (void *)0, + { PMDA_PMID(1,25), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.size.unknown */ +{ (void *)0, + { PMDA_PMID(1,66), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.client.total */ +{ (void *)0, + { PMDA_PMID(3,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.total */ +{ (void *)0, + { PMDA_PMID(3,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.cached.size.unknown */ +{ (void *)0, + { PMDA_PMID(3,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.total */ +{ (void *)0, + { PMDA_PMID(3,31), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,35), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,36), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,38), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,39), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,40), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.requests.uncached.size.unknown */ +{ (void *)0, + { PMDA_PMID(3,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* allservers.bytes.size.zero */ +{ (void *)0, + { PMDA_PMID(1,26), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le3k */ +{ (void *)0, + { PMDA_PMID(1,27), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le10k */ +{ (void *)0, + { PMDA_PMID(1,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le30k */ +{ (void *)0, + { PMDA_PMID(1,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le100k */ +{ (void *)0, + { PMDA_PMID(1,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le300k */ +{ (void *)0, + { PMDA_PMID(1,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le1m */ +{ (void *)0, + { PMDA_PMID(1,32), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.le3m */ +{ (void *)0, + { PMDA_PMID(1,33), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.size.gt3m */ +{ (void *)0, + { PMDA_PMID(1,34), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.total */ +{ (void *)0, + { PMDA_PMID(3,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.total */ +{ (void *)0, + { PMDA_PMID(3,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(3,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(3,73), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(3,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(3,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(3,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(3,77), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(3,78), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(3,79), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* allservers.bytes.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(3,80), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* + * web.perserver + */ + +/* perserver.watched */ +{ (void *)0, + { PMDA_PMID(0,35), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) } }, + +/* perserver.numlogs */ +{ (void *)0, + { PMDA_PMID(2,36), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.errors */ +{ (void *)0, + { PMDA_PMID(2,37), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.total */ +{ (void *)0, + { PMDA_PMID(2,38), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.get */ +{ (void *)0, + { PMDA_PMID(2,39), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.head */ +{ (void *)0, + { PMDA_PMID(2,40), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.post */ +{ (void *)0, + { PMDA_PMID(2,41), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.other */ +{ (void *)0, + { PMDA_PMID(2,42), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.bytes.total */ +{ (void *)0, + { PMDA_PMID(2,43), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.get */ +{ (void *)0, + { PMDA_PMID(2,44), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.head */ +{ (void *)0, + { PMDA_PMID(2,45), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.post */ +{ (void *)0, + { PMDA_PMID(2,46), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.other */ +{ (void *)0, + { PMDA_PMID(2,47), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.requests.size.zero */ +{ (void *)0, + { PMDA_PMID(2,48), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le3k */ +{ (void *)0, + { PMDA_PMID(2,49), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le10k */ +{ (void *)0, + { PMDA_PMID(2,50), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le30k */ +{ (void *)0, + { PMDA_PMID(2,51), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le100k */ +{ (void *)0, + { PMDA_PMID(2,52), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le300k */ +{ (void *)0, + { PMDA_PMID(2,53), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le1m */ +{ (void *)0, + { PMDA_PMID(2,54), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.le3m */ +{ (void *)0, + { PMDA_PMID(2,55), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.gt3m */ +{ (void *)0, + { PMDA_PMID(2,56), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.size.unknown */ +{ (void *)0, + { PMDA_PMID(2,67), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.client.total */ +{ (void *)0, + { PMDA_PMID(4,1), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.total */ +{ (void *)0, + { PMDA_PMID(4,11), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,12), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,13), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,14), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,15), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,16), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,17), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,18), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,19), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,20), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.cached.size.unknown */ +{ (void *)0, + { PMDA_PMID(4,21), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.total */ +{ (void *)0, + { PMDA_PMID(4,31), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,32), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,33), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,34), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,35), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,36), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,37), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,38), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,39), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,40), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.requests.uncached.size.unknown */ +{ (void *)0, + { PMDA_PMID(4,41), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) } }, + +/* perserver.bytes.size.zero */ +{ (void *)0, + { PMDA_PMID(2,57), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le3k */ +{ (void *)0, + { PMDA_PMID(2,58), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le10k */ +{ (void *)0, + { PMDA_PMID(2,59), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le30k */ +{ (void *)0, + { PMDA_PMID(2,60), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le100k */ +{ (void *)0, + { PMDA_PMID(2,61), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le300k */ +{ (void *)0, + { PMDA_PMID(2,62), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le1m */ +{ (void *)0, + { PMDA_PMID(2,63), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.le3m */ +{ (void *)0, + { PMDA_PMID(2,64), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.size.gt3m */ +{ (void *)0, + { PMDA_PMID(2,65), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.total */ +{ (void *)0, + { PMDA_PMID(4,51), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,52), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,53), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,54), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,55), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,56), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,57), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,58), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,59), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.cached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,60), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.total */ +{ (void *)0, + { PMDA_PMID(4,71), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.zero */ +{ (void *)0, + { PMDA_PMID(4,72), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le3k */ +{ (void *)0, + { PMDA_PMID(4,73), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le10k */ +{ (void *)0, + { PMDA_PMID(4,74), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le30k */ +{ (void *)0, + { PMDA_PMID(4,75), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le100k */ +{ (void *)0, + { PMDA_PMID(4,76), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le300k */ +{ (void *)0, + { PMDA_PMID(4,77), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le1m */ +{ (void *)0, + { PMDA_PMID(4,78), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.le3m */ +{ (void *)0, + { PMDA_PMID(4,79), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, + +/* perserver.bytes.uncached.size.gt3m */ +{ (void *)0, + { PMDA_PMID(4,80), PM_TYPE_U64, WEBLOG_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) } }, +/* + * Added in PCPWEB 1.1.1 + */ + +/* perserver.logidletime */ +{ (void *)0, + { PMDA_PMID(2,68), PM_TYPE_U32, WEBLOG_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) } }, + +}; + +/* number of metrics */ +static int numMetrics = (sizeof(wl_metrics)/sizeof(wl_metrics[0])); + +/* number of instance domains */ +static int numIndoms = sizeof(wl_indomTable)/sizeof(wl_indomTable[0]); + +/* mask to get the cluster from a PMID */ +static int _clusterMask = (1<<22) - (1<<10); + +/* refresh all logs if wl_updateAll != 0 */ +int wl_updateAll = 0; + +/* time in milliseconds to update all the logs */ +__uint32_t wl_catchupTime = 0; + +/* time log refresh was started */ +time_t wl_timeOfRefresh = 0; + +/* flag to indicate DSO or Daemon */ +int wl_isDSO = 0; + +/* request size categories */ +long wl_sizes[] = { + 0, 3*1024, 10*1024, 30*1024, 100*1024, 300*1024, 1024*1024, 3*1024*1024, 0 +}; + +#define BUFFER_LEN 2048 + +static pmdaExt *extp; /* set in web_init() */ + +#ifdef HAVE_SIGHUP +/* + * Signal handler for an sproc receiving TERM (probably from parent) + */ +static void +onhup(int s) +{ + _exit(s != SIGHUP); +} +#endif + +/* + * Replacement for fgets using the FileInfo structure + */ + +int +wl_gets(FileInfo *fip, char **line) +{ + char *p; + int nch; + int sts; + + if (fip->filePtr < 0) { + return -1; + } + + p = fip->bp; + +more: + while (p < fip->bend) { + if (*p == '\n') { + /* newline, we are done */ + *p++ = '\0'; + *line = fip->bp; + fip->bp = p; + return p - *line; + } + p++; + } + + /* out the end of the buffer, and no newline */ + nch = fip->bend - fip->bp; + if (nch == FIBUFSIZE) { + /* buffer full, and no newline! ... truncate and return */ + fip->buf[FIBUFSIZE-1] = '\n'; + p = &fip->buf[FIBUFSIZE-1]; + goto more; + } + if (nch) + /* shuffle partial line to start of buffer */ + memcpy(fip->buf, fip->bp, nch); + fip->bp = fip->buf; + fip->bend = &fip->buf[nch]; + + /* refill */ + sts = read(fip->filePtr, fip->bend, FIBUFSIZE-nch); + if (sts <= 0) { + /* no more, either terminate last line, or really return status */ + if (nch) { + *fip->bend = '\n'; + sts = 1; + } + else { + return sts; + } + } + p = fip->bend; + fip->bend = &fip->bend[sts]; + goto more; +} + +/* + * Open a log file and seek to the end + */ + +int +openLogFile(FileInfo *theFile) +{ + int diff = theFile->filePtr; + char *line = (char *)0; + + theFile->filePtr = open(theFile->fileName, O_RDONLY); + + if (theFile->filePtr == -1) { + if (theFile->filePtr != diff) { + logmessage(LOG_ERR, "openLogFile: open %s: %s\n", + theFile->fileName, osstrerror()); + } + return -1; + } + + if (fstat(theFile->filePtr, &(theFile->fileStat)) < 0) { + logmessage(LOG_ERR, "openLogFile: stat for %s: %s\n", + theFile->fileName, osstrerror()); + wl_close(theFile->filePtr); + return -1; + } + + logmessage(LOG_INFO, "%s opened (fd=%d, inode=%d)\n", + theFile->fileName, + theFile->filePtr, + theFile->fileStat.st_ino); + + /* throw away last line in file */ + if (theFile->fileStat.st_size != 0) { + lseek(theFile->filePtr, -2L, SEEK_END); + wl_gets(theFile, &line); + } + + if (fstat(theFile->filePtr, &(theFile->fileStat)) < 0) { + logmessage(LOG_ERR, "openLogFile: update stat for %s: %s\n", + theFile->fileName, osstrerror()); + wl_close(theFile->filePtr); + return -1; + } + +/* +* Check and warn if a log file has not been modified in the last 24 hours, +* as this may indicate something is wrong with the PMDA's configuration, +* or the Web server's configuration +*/ + + diff = time((time_t*)0) - theFile->fileStat.st_mtime; + if (diff > DORMANT_WARN) { + logmessage(LOG_WARNING, + "log file %s has not been modified for at least %d days", + theFile->fileName, + diff / DORMANT_WARN); + } + + return 0; +} + +/* + * Check the log file is still the correct log file. + * + * If the file has not been modified in wl_chkDelay seconds, then reopen + * the file and compare the inodes. + * Otherwise the current inode and size of the file are checked. + * + * Returns a LogFileCode indicating the status of the log file. + */ + +static int +checkLogFile(FileInfo *theFile, + struct stat *tmpStat) +{ + int tmpFd = -1; + int result = wl_ok; + +/* + * File is closed, if enough time has elasped since last attempt, try to + * open it + */ + + if (theFile->filePtr < 0) + { + if (wl_timeOfRefresh - theFile->lastActive > wl_chkDelay) + { + theFile->lastActive = wl_timeOfRefresh; + if (openLogFile(theFile) < 0) + result = wl_unableToOpen; + else + result = wl_opened; + } + else + result = wl_closed; + } + +/* Get the file stat info on the open file */ + + if (theFile->filePtr >= 0) { + if (fstat(theFile->filePtr, tmpStat) < 0) { + logmessage(LOG_ERR, "checkLogFile: stat on open %s: %s\n", + theFile->fileName, osstrerror()); + wl_close(theFile->filePtr); + result = wl_unableToStat; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, + "checkLogFile: could not stat %s\n", + theFile->fileName); + } +#endif + + } + } + +/* + * Check that we are dealing with a regular file. If is a character + * device or directory etc just ignore it. + */ + + if (result == wl_ok && !(theFile->fileStat.st_mode & S_IFREG)) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "%s is not a regular file. Skipping...\n", + theFile->fileName); +#endif + + result = wl_irregularFile; + } + +/* + * Check that the size hasn't gotten any smaller e.g. from ftruncate(2) + */ + + if (result == wl_ok && tmpStat->st_size < theFile->fileStat.st_size) { + + logmessage(LOG_WARNING, + "%s stat - inode %d, size %d -> %d\n", + theFile->fileName, + theFile->fileStat.st_ino, + theFile->fileStat.st_size, + tmpStat->st_size); + + result = wl_reopened; + } + +/* + * File was already open, check to see if it hasn't been modified, and + * that the last time we checked it was > wl_chkDelay ago. + */ + + if (result == wl_ok && + tmpStat->st_mtime == theFile->fileStat.st_mtime && + wl_timeOfRefresh - theFile->lastActive > wl_chkDelay) { + + tmpFd = open(theFile->fileName, O_RDONLY); + + if (tmpFd < 0) { + logmessage(LOG_ERR, + "checkLogFile: 2nd open to %s: %s\n", + theFile->fileName, + osstrerror()); + wl_close(theFile->filePtr); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, + "checkLogFile: could not check %s\n", + theFile->fileName); + } +#endif + + } + else if (fstat(tmpFd, tmpStat) < 0) { + logmessage(LOG_ERR, + "checkLogFile: stat on inactive %s: %s\n", + theFile->fileName, + osstrerror()); + wl_close(theFile->filePtr); + result = wl_unableToStat; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + logmessage(LOG_DEBUG, + "checkLogFile: could not stat inactive %s\n", + theFile->fileName); + } +#endif + + } + else if (tmpStat->st_ino != theFile->fileStat.st_ino) { + + logmessage(LOG_WARNING, + "%s inactive - inode %d -> %d\n", + theFile->fileName, + theFile->fileStat.st_ino, + tmpStat->st_ino); + + result = wl_reopened; + } + else + theFile->lastActive = wl_timeOfRefresh; + + + if (tmpFd >= 0) + close(tmpFd); + } + + +/* + * File needs to be reopened due to change in inode, smaller size, or lack + * of activity + */ + + if (result == wl_reopened) { + + theFile->lastActive = wl_timeOfRefresh; + + wl_close(theFile->filePtr); + + if (openLogFile(theFile) < 0) { + + logmessage(LOG_WARNING, + "checkLogFile: unable to reopen %s\n", + theFile->fileName); + result = wl_unableToOpen; + } + +/* update the stat information using new file desc */ + + else if (fstat(theFile->filePtr, tmpStat) < 0) { + logmessage(LOG_ERR, + "checkLogFile - stat on reopened %s: %s\n", + theFile->fileName, + osstrerror()); + wl_close(theFile->filePtr); + result = wl_unableToStat; + } + } + +/* if the size has increased in the logs, then change the lastActive time */ + + if (result == wl_ok && tmpStat->st_mtime > theFile->fileStat.st_mtime) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "%s grew %d bytes\n", + theFile->fileName, + tmpStat->st_size - theFile->fileStat.st_size); +#endif + theFile->lastActive = wl_timeOfRefresh; + } + + return result; +} + +/* + * Main function for sprocs. Contains an infinite loop selecting on the pipe from the + * main process. Anything on the pipe indicates a refresh is required. + */ + +void +sprocMain(void *sprocNum) +{ + int mySprocNum = *((int*)sprocNum); + int i = 0; + int sts = 0; + WebSproc *sprocData = &wl_sproc[mySprocNum]; + WebServer *server = (WebServer*)0; + + /* Pause a sec' so the output log doesn't get mucked up */ + sleep(1); + +#ifdef HAVE_SIGHUP + /* SIGHUP when the parent dies */ + signal(SIGHUP, onhup); +#endif + +#ifdef HAVE_PRCTL +#ifdef HAVE_PR_TERMCHILD + prctl(PR_TERMCHILD); +#elif HAVE_PR_SET_PDEATHSIG + prctl(PR_SET_PDEATHSIG, SIGHUP); +#endif +#endif + +/* close channel to pmcd */ + if (__pmSocketIPC(extp->e_infd)) + __pmCloseSocket(extp->e_infd); + else if (close(extp->e_infd) < 0) { + logmessage(LOG_ERR, "sprocMain: pmcd ch. close(fd=%d) failed: %s\n", + extp->e_infd, osstrerror()); + } + if (__pmSocketIPC(extp->e_outfd)) + __pmCloseSocket(extp->e_outfd); + else if (close(extp->e_outfd) < 0) { + logmessage(LOG_ERR, "sprocMain: pmcd ch. close(fd=%d) failed: %s\n", + extp->e_outfd, osstrerror()); + } + +/* close pipes to main process which are not to be used */ + + if(close(sprocData->inFD[1]) < 0) { + logmessage(LOG_ERR, "sprocMain[%d]: pipe close(fd=%d) failed: %s\n", + mySprocNum, sprocData->inFD[1], osstrerror()); + } + if(close(sprocData->outFD[0]) < 0) { + logmessage(LOG_ERR, "sprocMain[%d]: pipe close(fd=%d) failed: %s\n", + mySprocNum, sprocData->outFD[0], osstrerror()); + } + +/* open up all file descriptors */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Sproc %d started for servers %d to %d\n", + mySprocNum, + sprocData->firstServer, + sprocData->lastServer); +#endif + + for (i=sprocData->firstServer; i<=sprocData->lastServer; i++) + { + server = &wl_servers[i]; + if (server->counts.active) { + openLogFile(&(server->access)); + openLogFile(&(server->error)); + } + } + +/* wait for message from pmda to probe files */ + + for (;;) { + sts = read(sprocData->inFD[0], &i, sizeof(i)); + if (sts <= 0) { + logmessage(LOG_ERR, "Sproc[%d] read(fd=%d) failed: %s\n", + mySprocNum, sprocData->inFD[0], osstrerror()); + exit(1); + } + refresh(sprocData); + sts = write(sprocData->outFD[1], &i, sizeof(i)); + if (sts <= 0) { + logmessage(LOG_ERR, "Sproc[%d] write(fd=%d) failed: %s\n", + sprocData->outFD[1], mySprocNum, osstrerror()); + exit(1); + } + } +} + +/* + * Refresh all the server log files that this process monitors. + * Any entries are parsed, categorised and added to the appropriate metrics. + */ + +void +refresh(WebSproc* proc) +{ + struct stat tmpStat; + + WebServer *server = (WebServer *)0; + WebCount *count = (WebCount *)0; + FileInfo *accessFile = (FileInfo *)0; + FileInfo *errorFile = (FileInfo *)0; + + char *line = (char *)0; + char *end = (char *)0; + int httpMethod = 0; + long size = 0; + int sizeIndex = 0; + int newLength = 0; + int i = 0; + int sts = 0; + int result = wl_ok; + int ok = 0; + time_t currentTime; + size_t nmatch = 5; + regmatch_t pmatch[5]; + + + currentTime = time((time_t*)0); + +/* iterate through each flagged server */ + + for (i=proc->firstServer; i<=proc->lastServer; i++) { + + server = &(wl_servers[i]); + accessFile = &(server->access); + errorFile = &(server->error); + + if ((server->update || wl_updateAll) && server->counts.active) { + + server->counts.numLogs = 0; + +/* check access log still exists */ + + result = checkLogFile(accessFile, &tmpStat); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "checkLogFile returned %d for server %d (access)\n", + result, + i); +#endif + +/* scan access log */ + + if (result == wl_ok || result == wl_reopened || + result == wl_opened) { + + server->counts.numLogs++; + server->counts.modTime = (__uint32_t)(currentTime - + tmpStat.st_mtime); + + while (accessFile->fileStat.st_size < tmpStat.st_size) { + + sts = wl_gets(accessFile, &line); + if (sts <= 0) { + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, + "Short read of %s by %d bytes\n", + accessFile->fileName, + tmpStat.st_size - accessFile->fileStat.st_size); +#endif + + if (sts == 0) { + logmessage(LOG_WARNING, + "refresh %s: unexpected eof\n", + accessFile->fileName); + } + else { + logmessage(LOG_ERR, "refresh %s: %s\n", + accessFile->fileName, osstrerror()); + } + + wl_close(accessFile->filePtr); + accessFile->lastActive -= wl_chkDelay; + break; + } + + accessFile->fileStat.st_size += sts; + + if (proc->strLength == 0 || proc->strLength <= sts) + newLength = sts > 255 ? ((sts / 256) + 1) * 256 : 256; + else + newLength = proc->strLength; + + if (newLength > proc->strLength) + { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Resizing strings from %d to %d bytes\n", + proc->strLength, + newLength); + } +#endif + proc->methodStr = (char*)realloc(proc->methodStr, + newLength * sizeof(char)); + proc->sizeStr = (char*)realloc(proc->sizeStr, + newLength * sizeof(char)); + proc->c_statusStr = (char*)realloc(proc->c_statusStr, + newLength * sizeof(char)); + proc->s_statusStr = (char*)realloc(proc->s_statusStr, + newLength * sizeof(char)); + proc->strLength = newLength; + } + + if (proc->methodStr == (char *)0 || + proc->sizeStr == (char *)0 || + proc->c_statusStr == (char *)0 || + proc->s_statusStr == (char *)0 ) { + logmessage(LOG_ERR, + "Unable to allocate %d bytes to strings", + newLength); + proc->strLength = 0; + if (proc->methodStr != (char *)0) + free(proc->methodStr); + if (proc->sizeStr != (char *)0) + free(proc->sizeStr); + if (proc->c_statusStr != (char *)0) + free(proc->c_statusStr); + if (proc->s_statusStr != (char *)0) + free(proc->s_statusStr); + + break; + } + + ok = 0; + + if (wl_regexTable[accessFile->format].posix_regexp) { + if (regexec(wl_regexTable[accessFile->format].regex, + line, nmatch, pmatch, 0) == 0) { + + if(pmatch[1].rm_so < 0 || pmatch[2].rm_so < 0) { + logmessage(LOG_ERR, + "failed to match method and size: %s\n", + line); + continue; + } + + if(server->counts.extendedp) { + if(pmatch[3].rm_so < 0 || pmatch[4].rm_so < 0) { + logmessage(LOG_ERR, + "failed to match status codes: %s\n", + line); + continue; + } + } + + line[pmatch[wl_regexTable[accessFile->format].methodPos].rm_eo] = '\0'; + strncpy(proc->methodStr, &line[pmatch[wl_regexTable[accessFile->format].methodPos].rm_so], + (pmatch[wl_regexTable[accessFile->format].methodPos].rm_eo - + pmatch[wl_regexTable[accessFile->format].methodPos].rm_so) + 1); + + line[pmatch[wl_regexTable[accessFile->format].sizePos].rm_eo] = '\0'; + strncpy(proc->sizeStr, &line[pmatch[wl_regexTable[accessFile->format].sizePos].rm_so], + (pmatch[wl_regexTable[accessFile->format].sizePos].rm_eo - + pmatch[wl_regexTable[accessFile->format].sizePos].rm_so) + 1); + + if(server->counts.extendedp) { + line[pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_eo] = '\0'; + strncpy(proc->c_statusStr, &line[pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_so], + (pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_eo - + pmatch[wl_regexTable[accessFile->format].c_statusPos].rm_so) + 1); + + line[pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_eo] = '\0'; + strncpy(proc->s_statusStr, &line[pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_so], + (pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_eo - + pmatch[wl_regexTable[accessFile->format].s_statusPos].rm_so) + 1); + } else { + proc->c_statusStr[0] = '\0'; + proc->s_statusStr[0] = '\0'; + } + ok = 1; + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Regex failed on %s\n", line); +#endif + } +#ifdef NON_POSIX_REGEX + else if (regex(wl_regexTable[accessFile->format].np_regex, + line, proc->methodStr, proc->sizeStr, proc->c_statusStr, proc->s_statusStr) != NULL) { + ok = 1; + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Regex failed on %s\n", line); +#endif +#endif + if ( ok ) { + + for (line = proc->methodStr; *line; line++) + *line = toupper((int)*line); + + httpMethod = wl_httpOther; + switch(proc->methodStr[0]) { + case 'G': + if(strcmp(proc->methodStr, "GET") == 0) { + httpMethod = wl_httpGet; + } + break; + case 'O': + if(strcmp(proc->methodStr, "O") == 0) { + httpMethod = wl_httpGet; + } + break; + case 'H': + if(strcmp(proc->methodStr, "HEAD") == 0) { + httpMethod = wl_httpHead; + } + break; + case 'P': + if(strcmp(proc->methodStr, "POST") == 0 || + strcmp(proc->methodStr, "PUT") == 0) { + httpMethod = wl_httpPost; + } + break; + case 'I': + if(strcmp(proc->methodStr, "I") == 0) { + httpMethod = wl_httpPost; + } + break; + } + + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + + count = &(server->counts); + count->methodReq[httpMethod]++; + count->methodBytes[httpMethod] += size; + + count->sizeReq[sizeIndex]++; + count->sizeBytes[sizeIndex] += size; + + count->sumReq++; + count->sumBytes += size; + + if(server->counts.extendedp == 1) { + /* common extended format */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, line=%s [CEF]\n M: %s S: %s CS: %s, SS: %s", + i, + line, + proc->methodStr, + proc->sizeStr, + proc->c_statusStr, + proc->s_statusStr); + } +#endif + + /* + * requested page is not in client/browser cache, nor in the + * server's cache so it has been fetched from the remote server + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + strcmp(proc->s_statusStr, "200") == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, REMOTE fetch: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->uncached_sumReq++; + count->uncached_sumBytes += size; + count->uncached_sizeReq[sizeIndex]++; + count->uncached_sizeBytes[sizeIndex] += size; + + } + + /* + * requested page is not in client/browser cache, but is in the + * server's cache so it is just returned to the client (a cache hit) + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + (strcmp(proc->s_statusStr, "304") == 0 || + strcmp(proc->s_statusStr, "-") == 0)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CACHE return: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->cached_sumReq++; + count->cached_sumBytes += size; + count->cached_sizeReq[sizeIndex]++; + count->cached_sizeBytes[sizeIndex] += size; + + } + + /* + * requested page is in client/browser cache + */ + if(strcmp(proc->c_statusStr, "304") == 0 && + (strcmp(proc->s_statusStr, "304") == 0 || + strcmp(proc->s_statusStr, "-") == 0)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CLIENT hit\n", + i); + } +#endif + count->client_sumReq++; + } + } else if(server->counts.extendedp == 2) { + /* default squid format */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, line=%s [squid]\n M: %s S: %s CS: %s, SS: %s", + i, + line, + proc->methodStr, + proc->sizeStr, + proc->c_statusStr, + proc->s_statusStr); + } +#endif + + /* + * requested page is not in client/browser cache, nor in the + * server's cache so it has been fetched from the remote server + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + (strstr(proc->s_statusStr, "_MISS") != NULL || + strstr(proc->s_statusStr, "_CLIENT_REFRESH") != NULL || + strstr(proc->s_statusStr, "_SWAPFAIL") != NULL)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, REMOTE fetch: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->uncached_sumReq++; + count->uncached_sumBytes += size; + count->uncached_sizeReq[sizeIndex]++; + count->uncached_sizeBytes[sizeIndex] += size; + + } + + /* + * requested page is not in client/browser cache, but is in the + * server's cache so it is just returned to the client (a cache hit) + */ + if(strcmp(proc->c_statusStr, "200") == 0 && + strstr(proc->s_statusStr, "_HIT") != NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CACHE return: of %.0f bytes\n", + i, + atof(proc->sizeStr)); + } +#endif + /* + * now bucket the size + */ + if (strcmp(proc->sizeStr, "-") == 0 || + strcmp(proc->sizeStr, " ") == 0) { + size = 0; + sizeIndex = wl_unknownSize; + } + else { + size = strtol(proc->sizeStr, &end, 10); + if (*end != '\0') { + logmessage(LOG_ERR, "Bad size (%s) @ %s", + proc->sizeStr, + line); + continue; + } + + for (sizeIndex = 0; + sizeIndex < wl_gt3m && size > wl_sizes[sizeIndex]; + sizeIndex++); + } + count->cached_sumReq++; + count->cached_sumBytes += size; + count->cached_sizeReq[sizeIndex]++; + count->cached_sizeBytes[sizeIndex] += size; + } + + /* + * requested page is in client/browser cache + */ + if(strcmp(proc->c_statusStr, "304") == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, CLIENT hit\n", + i); + } +#endif + count->client_sumReq++; + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, + "Access: Server=%d, line=%s\n method=%s [%d], size=%s=%d [%d]\n", + i, + line, + proc->methodStr, + httpMethod, + proc->sizeStr, + size, + sizeIndex); + } +#endif + + } + } + accessFile->fileStat = tmpStat; + } + + result = checkLogFile(errorFile, &tmpStat); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, + "checkLogFile returned %d for server %d (error)\n", + result, + i); +#endif + + /* scan error log */ + + if (result == wl_ok || result == wl_reopened || + result == wl_opened) { + + server->counts.numLogs++; + + while (errorFile->fileStat.st_size < tmpStat.st_size) { + sts = wl_gets(errorFile, &line); + if (sts <= 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + logmessage(LOG_DEBUG, "%s was %d bytes short\n", + errorFile->fileName, + tmpStat.st_size - + errorFile->fileStat.st_size); +#endif + + if (sts < 0) { + logmessage(LOG_ERR, "refresh %s: %s\n", + errorFile->fileName, osstrerror()); + } + else { + logmessage(LOG_WARNING, + "refresh %s: unexpected eof\n", + errorFile->fileName); + } + + wl_close(errorFile->filePtr); + errorFile->lastActive -= wl_chkDelay; + break; + } + + errorFile->fileStat.st_size += sts; + + if(wl_regexTable[errorFile->format].posix_regexp) { + if (regexec(wl_regexTable[errorFile->format].regex, + line, nmatch, pmatch, 0) == 0) { + server->counts.errors++; + } +#ifdef NON_POSIX_REGEX + } else { + if (regex(wl_regexTable[errorFile->format].np_regex, + line, proc->methodStr, proc->sizeStr) != NULL) { + server->counts.errors++; + } +#endif + } + } + errorFile->fileStat = tmpStat; + } + } + + /* check to see if a server is inactive but has a file open. It may + have just been deactivated */ + + else if ((server->update || wl_updateAll) && !server->counts.active) { + + if (accessFile->filePtr >= 0) { + + logmessage(LOG_WARNING, + "Closing inactive server %d access file: %s\n", + i, + accessFile->fileName); + + wl_close(accessFile->filePtr); + } + + if (errorFile->filePtr >= 0) { + + logmessage(LOG_WARNING, + "Closing inactive server %d error file: %s\n", + i, + errorFile->fileName); + + wl_close(errorFile->filePtr); + } + } + } +} + +/* + * Initialise the indom and meta tables + * Check that we can do direct mapping. + */ + +/* + * Mark servers that are required in the latest profile. + */ +static int +web_profile(__pmProfile *prof, pmdaExt *ext) +{ + pmdaIndom *idp = wl_indomTable; + int j; + + ext->e_prof = prof; + for (j = 0; j < idp->it_numinst; j++) { + if (__pmInProfile(idp->it_indom, prof, idp->it_set[j].i_inst)) + wl_servers[j].update = 1; + else + wl_servers[j].update = 0; + } + + return 0; +} + +/* + * Probe servers for log file changes. + * Only those servers that are marked will be requsted. Therefore, if an sproc does + * not have any marked servers, it will not be signalled. + * NOTE: The main process completes its refresh before signalling the other sprocs. + */ + +void +probe(void) +{ + int i = 0; + int j = 0; + int sts = 0; + int dummy = 1; + int sprocsUsed = 0; + int nfds = 0; + fd_set rfds; + fd_set tmprfds; + int thisFD; + WebSproc *sprocData = (WebSproc*)0; + struct timeval theTime; + + __pmtimevalNow(&theTime); + + wl_timeOfRefresh = theTime.tv_sec; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Starting probe at %d\n", wl_timeOfRefresh); +#endif + + FD_ZERO(&rfds); + +/* + * Determine which sprocs have servers that must be refreshed. + * Add those sprocs pipes to the file descriptor list. + */ + + for (i=1; i<=wl_numSprocs; i++) { + sprocData = &wl_sproc[i]; + + if (!wl_updateAll) { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].update) + break; + } + else { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].counts.active) + break; + } + + if (j > sprocData->lastServer) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Skipping sproc %d\n", i); +#endif + continue; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, + "Told sproc %d to probe for at least server %d\n", + i, + j); +#endif + + sprocsUsed++; + thisFD = sprocData->outFD[0]; + sts = write(sprocData->inFD[1], &dummy, sizeof(dummy)); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch write(fd=%d): %s", + sprocData->inFD[1], osstrerror()); + exit(1); + } + + FD_SET(thisFD, &rfds); + nfds = nfds < (thisFD + 1) ? thisFD + 1 : nfds; + } + +/* + * Check that we have to update the main process servers + */ + + sprocData = &wl_sproc[0]; + if (!wl_updateAll) { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].update) + break; + } + else { + for (j=sprocData->firstServer; j<=sprocData->lastServer; j++) + if (wl_servers[j].counts.active) + break; + } + + if (j <= sprocData->lastServer) { + refresh(&wl_sproc[0]); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Done probe for 0 to %d\n", + sprocData->lastServer); +#endif + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL2) + logmessage(LOG_DEBUG, "Skipping refresh of main process\n"); +#endif + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + logmessage(LOG_DEBUG, "Waiting for reply from %d out of %d sprocs\n", + sprocsUsed, + wl_numSprocs); + } +#endif + +/* + * Wait for all sprocs to reply + * Note: This could get into a hard select loop if an sproc losses it + */ + + for (i=0; ioutFD[0]; + + if (FD_ISSET(thisFD, &tmprfds)) { + FD_CLR(sprocData->outFD[0], &rfds); + sts = read(thisFD, &dummy, sizeof(dummy)); + if (sts < 0) { + logmessage(LOG_ERR, "Error on fetch read: %s", + osstrerror()); + exit(1); + } + } + } + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Finished probe\n"); +#endif +} + +/* + * Refresh all servers + * Usually called if no fetches have been received after a set time + */ + +void +refreshAll(void) +{ + struct timeval before; + struct timeval after; + + wl_updateAll = 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + logmessage(LOG_DEBUG, "Starting a refreshAll\n"); + } +#endif + + __pmtimevalNow(&before); + probe(); + + __pmtimevalNow(&after); + wl_catchupTime = (after.tv_sec - before.tv_sec) * 1000; + wl_catchupTime += (after.tv_usec - before.tv_usec) / 1000; + + wl_updateAll = 0; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "Probed all logs, took %d msec\n", + wl_catchupTime); +#endif + +} + +/* + * Build a pmResult table of the requested metrics + */ + +static int +web_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *ext) +{ + int i; /* over pmidlist[] */ + int j; /* over vset->vlist[] */ + int s; /* over server */ + int sts; + int need; + int inst; + int numval; + static pmResult *res = (pmResult *)0; + static int maxnpmids = 0; + pmValueSet *vset = (pmValueSet *)0; + pmDesc *dp = (pmDesc *)0; + __pmID_int *pmidp; + pmAtomValue atom; + int haveValue = 0; + int type; + __psint_t m_offset = 0; /* initialize to pander to gcc */ + int m_type = 0; /* initialize to pander to gcc */ + int cluster; + __uint32_t tmp32; + __uint64_t tmp64; + +/* determine if the total aggregates are required, which forces a refresh + of all servers, and if a probe is require at all */ + + j = 0; + for (i = 0; i < numpmid; i++) { + pmidp = (__pmID_int *)&pmidlist[i]; + if (pmidp->cluster == 1) + break; + else + j += pmidp->cluster; + } + + if (i < numpmid) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "web_fetch: refreshAll\n"); +#endif + refreshAll(); + } + else if (j) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "web_fetch: probe\n"); +#endif + probe(); + } +#ifdef PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL1) + logmessage(LOG_DEBUG, "web_fetch: no probes required\n"); +#endif + + + if (numpmid > maxnpmids) { + if (res != (pmResult *)0) + free(res); + +/* (numpmid - 1) because there's room for one valueSet in a pmResult */ + + need = sizeof(pmResult) + (numpmid - 1) * sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == (pmResult *)0) + return -oserror(); + maxnpmids = numpmid; + } + + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + +/* + * Get each corresponding metric from the meta table. + * Check that the metric has the correct cluster + */ + + for (i = 0; i < numpmid; i++) { + + pmidp = (__pmID_int*)&pmidlist[i]; + dp = (pmDesc *)0; + + if (ext->e_direct) { + + if (pmidp->item < numMetrics && + pmidlist[i] == wl_metrics[pmidp->item].m_desc.pmid) { + + dp = &wl_metrics[pmidp->item].m_desc; + m_offset = wl_metricInfo[pmidp->item].m_offset; + m_type = wl_metricInfo[pmidp->item].m_type; + } + } + else { + for (j = 0; jindom != PM_INDOM_NULL) { + numval = 0; + __pmdaStartInst(dp->indom, ext); + while(__pmdaNextInst(&inst, ext)) { + numval++; + } + } + else { + numval = 1; + } + } + + + /* Must use individual malloc()s because of pmFreeResult() */ + + if (numval >= 1) + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet) + + (numval - 1)*sizeof(pmValue)); + else + res->vset[i] = vset = (pmValueSet*)malloc(sizeof(pmValueSet) - + sizeof(pmValue)); + + if (vset == (pmValueSet *)0) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + + vset->pmid = pmidlist[i]; + vset->numval = numval; + vset->valfmt = PM_VAL_INSITU; + if (vset->numval <= 0) + continue; + + if (dp->indom == PM_INDOM_NULL) + inst = PM_IN_NULL; + else { + __pmdaStartInst(dp->indom, ext); + __pmdaNextInst(&inst, ext); + } + + type = dp->type; + pmidp = (__pmID_int *)&pmidlist[i]; + j = 0; + + do { + if (j == numval) { + + /* more instances than expected! */ + + numval++; + res->vset[i] = vset = (pmValueSet *)realloc(vset, + sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue)); + if (vset == (pmValueSet *)0) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + } + + vset->vlist[j].inst = inst; + + cluster = (dp->pmid & _clusterMask) >> 10; + haveValue = 1; + + switch(m_type) { + case wl_globalPtr: + atom.ul = *(__uint32_t *)(m_offset); + break; + + case wl_offset32: + if (wl_servers[inst].counts.active) + atom.ul = *((__uint32_t *)(((__psint_t)(&(wl_servers[inst].counts))) + m_offset)); + else + haveValue = 0; + break; + + case wl_offset64: + if (wl_servers[inst].counts.active) + atom.ull = *((__uint64_t *)(((__psint_t)(&(wl_servers[inst].counts))) + m_offset)); + else + haveValue = 0; + break; + + case wl_totalAggregate: + + if (wl_numActive == 0) + haveValue = 0; + else { + switch(m_offset) { + case 0: + /* errors */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) { + tmp32 += wl_servers[s].counts.errors; + } + } + atom.ul = tmp32; + break; + case 1: + /* requests */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) { + tmp32 += wl_servers[s].counts.sumReq; + } + } + atom.ul = tmp32; + break; + case 2: + /* bytes */ + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) { + tmp64 += wl_servers[s].counts.sumBytes; + } + } + atom.ull = tmp64; + break; + case 3: + /* client hit requests */ + tmp32 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.client_sumReq; + } + } + atom.ul = tmp32; + break; + case 4: + /* cached hit requests */ + tmp32 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.cached_sumReq; + } + } + atom.ul = tmp32; + break; + case 5: + /* cached hit requests */ + tmp32 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.uncached_sumReq; + } + } + atom.ul = tmp32; + break; + case 6: + /* cached hit bytes */ + tmp64 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.cached_sumBytes; + } + } + atom.ull = tmp64; + break; + case 7: + /* uncached bytes */ + tmp64 = haveValue = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.uncached_sumBytes; + } + } + atom.ull = tmp64; + break; + default: + break; + } + + } + break; + + case wl_serverAggregate: + + if (wl_servers[inst].counts.active == 0) + haveValue = 0; + else switch(m_offset) { + case 0: + /* requests */ + atom.ul = wl_servers[inst].counts.sumReq; + break; + case 1: + /* bytes */ + atom.ull = wl_servers[inst].counts.sumBytes; + break; + case 2: + /* client hit requests */ + if( wl_servers[inst].counts.extendedp ) { + atom.ul = wl_servers[inst].counts.client_sumReq; + } else + haveValue = 0; + break; + case 3: + /* cache hit requests */ + if( wl_servers[inst].counts.extendedp ) { + atom.ul = wl_servers[inst].counts.cached_sumReq; + } else + haveValue = 0; + break; + case 4: + /* uncached requests */ + if( wl_servers[inst].counts.extendedp ) { + atom.ul = wl_servers[inst].counts.uncached_sumReq; + } else + haveValue = 0; + break; + case 5: + /* cached bytes */ + if( wl_servers[inst].counts.extendedp ) { + atom.ull = wl_servers[inst].counts.cached_sumBytes; + } else + haveValue = 0; + break; + case 6: + /* uncached bytes */ + if( wl_servers[inst].counts.extendedp ) { + atom.ull = wl_servers[inst].counts.uncached_sumBytes; + } else + haveValue = 0; + default: + break; + } + break; + + case wl_requestMethod: + + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp32 += wl_servers[s].counts.methodReq[m_offset]; + atom.ul = tmp32; + } + } + else if (wl_servers[inst].counts.active) + atom.ul = wl_servers[inst].counts.methodReq[m_offset]; + else + haveValue = 0; + + break; + + case wl_bytesMethod: + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp64 += wl_servers[s].counts.methodBytes[m_offset]; + atom.ull = tmp64; + } + } + else if (wl_servers[inst].counts.active) + atom.ull = wl_servers[inst].counts.methodBytes[m_offset]; + else + haveValue = 0; + break; + + case wl_requestSize: + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp32 += wl_servers[s].counts.sizeReq[m_offset]; + atom.ul = tmp32; + } + } + else if (wl_servers[inst].counts.active) + atom.ul = wl_servers[inst].counts.sizeReq[m_offset]; + else + haveValue = 0; + break; + + case wl_bytesSize: + if (cluster == 1) { /* all servers */ + if (wl_numActive == 0) + haveValue = 0; + else { + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active) + tmp64 += wl_servers[s].counts.sizeBytes[m_offset]; + atom.ull = tmp64; + } + } + else if (wl_servers[inst].counts.active) + atom.ull = wl_servers[inst].counts.sizeBytes[m_offset]; + else + haveValue = 0; + break; + + case wl_requestCachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp32 += wl_servers[s].counts.cached_sizeReq[m_offset]; + } + atom.ul = tmp32; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ul = wl_servers[inst].counts.cached_sizeReq[m_offset]; + } + break; + + case wl_bytesCachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.cached_sizeBytes[m_offset]; + } + } + atom.ull = tmp64; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ull = wl_servers[inst].counts.cached_sizeBytes[m_offset]; + } + break; + + case wl_requestUncachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp ) { + haveValue = 1; + tmp32 += wl_servers[s].counts.uncached_sizeReq[m_offset]; + } + } + atom.ul = tmp32; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ul = wl_servers[inst].counts.uncached_sizeReq[m_offset]; + } + break; + + case wl_bytesUncachedSize: + haveValue = 0; + if (cluster == 3) { /* all servers */ + tmp64 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active && + wl_servers[s].counts.extendedp) { + haveValue = 1; + tmp64 += wl_servers[s].counts.uncached_sizeBytes[m_offset]; + } + } + atom.ull = tmp64; + } + else if (wl_servers[inst].counts.active && + wl_servers[inst].counts.extendedp) { + haveValue = 1; + atom.ull = wl_servers[inst].counts.uncached_sizeBytes[m_offset]; + } + break; + + case wl_watched: + atom.ul = *((__uint32_t *)(((__psint_t)(&(wl_servers[inst].counts))) + m_offset)); + break; + case wl_numAlive: + tmp32 = 0; + for (s = 0; s < wl_numServers; s++) { + if (wl_servers[s].counts.active) + tmp32 += wl_servers[s].counts.numLogs > 0 ?1:0; + } + atom.ul = tmp32; + break; + + case wl_nosupport: + haveValue = 0; + break; + + default: + logmessage(LOG_CRIT, + "Illegal Meta Type (%d) for metric %d\n", + m_type, + pmidp->item); + exit(1); + } + + if (haveValue) { + sts = __pmStuffValue(&atom, &vset->vlist[j], type); + if (sts < 0) { + __pmFreeResultValues(res); + return sts; + } + + vset->valfmt = sts; + j++; /* next element in vlist[] for next instance */ + } + + } while (dp->indom != PM_INDOM_NULL && __pmdaNextInst(&inst, ext)); + + vset->numval = j; + } + *resp = res; + return 0; +} + +/* + * Store into one of three metrics: + * web.activity.config.catchup, web.activity.config.check and web.activity.server.watched + */ + +static int +web_store(pmResult *result, pmdaExt *ext) +{ + int i; + int j; + pmValueSet *vsp; + int sts = 0; + __pmID_int *pmidp; + WebServer *server = (WebServer*)0; + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + if (pmidp->cluster == 0) { + if (pmidp->item == 1) { /* web.activity.config.catchup */ + int val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 20; + } + wl_refreshDelay = val; + } + else if (pmidp->item == 3) {/* web.activity.config.check */ + int val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 20; + } + wl_chkDelay = val; + } + else if (pmidp->item == 35) {/* web.activity.server.watched */ + for (j = 0; j < vsp->numval; j++) { + int val = vsp->vlist[j].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 1; + } + + server = &wl_servers[vsp->vlist[j].inst]; + if (val > 0 && server->counts.active == 0) { + wl_numActive++; + server->counts.active = 1; + } + else if (val == 0 && server->counts.active > 0){ + wl_numActive--; + server->counts.active = 0; + } + } + } + else { + sts = PM_ERR_PMID; + break; + } + } + else { + /* not one of the metrics we are willing to change */ + sts = PM_ERR_PMID; + break; + } + } + return sts; +} + +/* + * Initialise the callback table, and open the help text. + * This also calls the routine that to initialise the indom and meta tables. + */ + +void +web_init(pmdaInterface *dp) +{ + int m; + int type; + + extp = dp->version.two.ext; + + if (wl_isDSO) + pmdaDSO(dp, PMDA_INTERFACE_2, "weblog DSO", wl_helpFile); + + if (dp->status != 0) + return; + + dp->version.two.profile = web_profile; + dp->version.two.fetch = web_fetch; + dp->version.two.store = web_store; + + if (numMetrics != (sizeof(wl_metricInfo)/sizeof(wl_metricInfo[0]))) { + logmessage(LOG_CRIT, + "Metric and Metric Info tables are not the same size\n"); + dp->status = -1; + return; + } + + pmdaInit(dp, wl_indomTable, numIndoms, wl_metrics, numMetrics); + + for (m = 0; m < numMetrics; m++) { + type = wl_metricInfo[m].m_type; + if (type == wl_offset32 || type == wl_offset64 || + type == wl_watched) { + wl_metricInfo[m].m_offset -= (__psint_t)&dummyCount; + } + } + + return; +} + diff --git a/src/pmdas/weblog/weblog.h b/src/pmdas/weblog/weblog.h new file mode 100644 index 0000000..a2c99dd --- /dev/null +++ b/src/pmdas/weblog/weblog.h @@ -0,0 +1,140 @@ + +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _WEBLOG_H +#define _WEBLOG_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include +#include + +enum HTTP_Methods { + wl_httpGet, wl_httpHead, wl_httpPost, wl_httpOther, wl_numMethods +}; + +enum HistSizes { + wl_zero, wl_le3k, wl_le10k, wl_le30k, wl_le100k, wl_le300k, wl_le1m, + wl_le3m, wl_gt3m, wl_unknownSize, wl_numSizes +}; + +#define FIBUFSIZE 16*1024 +#define DORMANT_WARN 86400 + +typedef struct { + char* fileName; + int filePtr; + struct stat fileStat; + char buf[FIBUFSIZE]; + char *bp; + char *bend; + u_int format; /* index into regex for parsing file */ + time_t lastActive; /* time in sec when last active */ +} FileInfo; + +typedef struct { + __uint32_t methodReq[wl_numMethods]; + __uint64_t methodBytes[wl_numMethods]; + __uint32_t sizeReq[wl_numSizes]; + __uint64_t sizeBytes[wl_numSizes]; + __uint32_t cached_sizeReq[wl_numSizes]; + __uint64_t cached_sizeBytes[wl_numSizes]; + __uint32_t uncached_sizeReq[wl_numSizes]; + __uint64_t uncached_sizeBytes[wl_numSizes]; + __uint32_t sumReq; + __uint32_t client_sumReq; + __uint32_t cached_sumReq; + __uint32_t uncached_sumReq; + __uint64_t sumBytes; + __uint64_t cached_sumBytes; + __uint64_t uncached_sumBytes; + __uint32_t errors; + __uint32_t active; + __uint32_t numLogs; + __uint32_t modTime; + __uint32_t extendedp; +} WebCount; + +typedef struct { + int update; /* flag for updating this server */ + WebCount counts; + FileInfo access; + FileInfo error; +} WebServer; + +typedef struct { + int id; + pid_t pid; + int firstServer; + int lastServer; + int inFD[2]; + int outFD[2]; + char *methodStr; + char *sizeStr; + char *c_statusStr; + char *s_statusStr; + int strLength; +} WebSproc; + +typedef struct { + char* name; +#ifdef NON_POSIX_REGEX + char *np_regex; +#endif + regex_t* regex; + int methodPos; + int sizePos; + int c_statusPos; + int s_statusPos; + int posix_regexp; +} WebRegex; + +extern WebServer *wl_servers; +extern WebSproc *wl_sproc; +extern WebRegex *wl_regexTable; +extern pmdaInstid *wl_serverInst; +extern pmdaIndom wl_indomTable[]; + +extern __uint32_t wl_numServers; +extern __uint32_t wl_numActive; +extern __uint32_t wl_refreshDelay; +extern __uint32_t wl_chkDelay; +extern __uint32_t wl_sprocThresh; +extern __uint32_t wl_numSprocs; +extern __uint32_t wl_catchupTime; +extern __uint32_t wl_numRegex; + +extern int wl_updateAll; +extern time_t wl_timeOfProbe; +extern char *wl_logFile; +extern char wl_helpFile[]; +extern int wl_isDSO; + +int openLogFile(FileInfo*); +void probe(void); +void refresh(WebSproc*); +void refreshAll(void); +void sprocMain(void*); +void web_init(pmdaInterface*); +void logmessage(int, const char *, ...); + +#define wl_close(fd) do { if (fd >= 0) close(fd); fd = -1; } while (0) + +#endif /* _WEBLOG_H */ diff --git a/src/pmdas/weblog/weblogconv.sh b/src/pmdas/weblog/weblogconv.sh new file mode 100755 index 0000000..d62d2d2 --- /dev/null +++ b/src/pmdas/weblog/weblogconv.sh @@ -0,0 +1,62 @@ +#! /bin/sh +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# +# weblogconv: convert weblog.conf files to posix regex +# + +# only needed on Linux + +progname=$0 + +if [ $# -gt 0 -a "$1" = "-?" ]; then + echo "Usage: $progname infile [outfile]" + exit 0 +fi + +if [ $# -gt 2 ]; then + echo "$progname: Too many arguments." + exit 1 +fi + +if [ $# -lt 1 ] ; then + infile="" +else + infile=$1 +fi + +if [ $# -lt 2 ]; then + outfile="" +else + outfile="> $2" +fi + +if [ -n "$infile" -a ! -r "$infile" ]; then + echo "$progname: cannot read $infile" + exit 1 +fi + +sed \ + -e '/)\$[01]/!s/^regex[ \t][ \t]*\([^ \t][^ \t]*\)[ \t][ \t]*/regex_posix \1 - /' \ + -e 's/^regex[ \t][ \t]*\([^ \t][^ \t]*\)[ \t][ \t]*\(.*$0.*$1.*\)/regex_posix \1 method,size \2/' \ + -e 's/^regex[ \t][ \t]*\([^ \t][^ \t]*\)[ \t][ \t]*\(.*$1.*$0.*\)/regex_posix \1 size,method \2/' \ + -e 's/)$0/)/g' \ + -e 's/)$1/)/g' $infile | eval cat $outfile + +exit 0 diff --git a/src/pmdas/windows/GNUmakefile b/src/pmdas/windows/GNUmakefile new file mode 100644 index 0000000..aef7163 --- /dev/null +++ b/src/pmdas/windows/GNUmakefile @@ -0,0 +1,81 @@ +# +# Copyright (c) 2008-2009 Aconex. All Rights Reserved. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs +WINDIR = $(TOPDIR)/src/win32ctl + +IAM = windows +DOMAIN = WINDOWS +CFILES = pmda.c error.c open.c instance.c fetch.c helptext.c +HFILES = hypnotoad.h +LCFLAGS = -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0500 \ + -I$(WINDIR)/include +LLDFLAGS = -L$(WINDIR)/lib +LLDLIBS = $(PCP_PMDALIB) -lpcp_pdh +PMNS = pmns.disk pmns.kernel pmns.mem pmns.network \ + pmns.sqlserver pmns.filesys pmns.hinv pmns.pmda \ + pmns.process +LSRCFILES = $(PMNS) root help README pdhlist.c pdhmatch.sh +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LIBTARGET = pmda_windows.dll +EXTRATARGETS = pdhlist.exe help.dir help.pag + +LDIRT = root_windows domain.h $(IAM).log pmda$(IAM) \ + $(EXTRATARGETS) pdhlist.o + +CONF_LINE = "windows 79 dso windows_init $(PCP_PMDAS_DIR)/windows/pmda_windows.dll" + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "mingw" +build-me: root_windows $(LSRCFILES) $(LIBTARGET) $(EXTRATARGETS) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 pdhlist.exe $(LIBTARGET) $(PMDADIR) + $(INSTALL) -m 644 README root $(PMNS) $(PMDADIR) + $(INSTALL) -m 644 domain.h help.dir help.pag help $(PMDADIR) + $(INSTALL) -m 644 root_windows $(PCP_VAR_DIR)/pmns/root_windows +else +build-me: +install: +endif + +help.dir help.pag : help root_windows + $(RUN_IN_BUILD_ENV) $(TOPDIR)/src/newhelp/newhelp -n root_windows -v 2 -o help < help + +default_pcp: default + +install_pcp: install + +root_windows: ../../pmns/stdpmid $(PMNS) + rm -f root_windows + sed -e 's;;"../../pmns/stdpmid";' root_windows + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +$(OBJECTS): hypnotoad.h domain.h + +pdhlist.exe: pdhlist.o error.o + $(CCF) -o pdhlist.exe pdhlist.o error.o $(LLDFLAGS) -lpcp_pdh diff --git a/src/pmdas/windows/README b/src/pmdas/windows/README new file mode 100644 index 0000000..a6847e7 --- /dev/null +++ b/src/pmdas/windows/README @@ -0,0 +1,171 @@ +Windows PMDA +============ + +This PMDA collects performance data from a Microsoft Windows kernel. + +Metrics +======= + +The help text is exported from the kernel via the PDH (Performance +Data Helper) APIs. To view the help text, install the PMDA, then +the following command will list all the available metrics and their +explanatory "help" text: + + $ pminfo -fT kernel disk mem network filesys sqlserver hinv pmda process + +Installation +============ + + + # cd $PCP_PMDAS_DIR/windows + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options -- everything else is automated. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/windows + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/windows.log) should be checked for any warnings + or errors. + +Adding New Metrics +================== + +The following steps should be followed when adding new metrics ... this +assumes the MinGW gcc compiler is being used. + +a. Make sure you know what you've currently got, so on the target system + $ ./pdhlist | ./pdhmatch >status-quo + +b. Pick the Pdh paths you're interested in (drop the hostname prefix, + add the (*/*#*) or * patterns as appropriate. Beware that metrics + with multiple instances are reported multiple times ... for example + + SQLServer:Cache Manager\Cache Hit Ratio + SQLServer:Cache Manager(_Total)\Cache Hit Ratio + SQLServer:Cache Manager(*/*#*)\Cache Hit Ratio + + of these, the first pattern must not be used ... use the second one + if you want the "over all instances" totals, and the last one gives + the per instance metrics. + +c. Choose a PMID ... use the next unused number in sequence ... check + metricdesc[] in pmda.c, you want the PMDA_PMID(0,x) macro from the + last entry in the table, and the number you're after is x+1 (x+1 = + 81 for my example). + +d. Choose a name and where to put the new metric in the namespace ... + edit the appropriate pmns.* file (let's assume you're adding to one + of the existing Pdh groups, rather than creating a new one which is + more complicated), adding an appropriate entry using x+1 from above + for the last component of the PMID. Something like this in + pmns.sqlserver + + sqlserver { + ... + cache_mgr // new line + } + + ... + + // all new lines from here down + + sqlserver.cache_mgr { + all + percache + } + + sqlserver.cache_mgr.all { + cache_hit_ratio WINDOWS:0:81 + } + + sqlserver.cache_mgr.cache { + cache_hit_ratio WINDOWS:0:82 + } + + + Write the modified pmns file. + + Check you've got it right with + + $ pminfo -m -n root sqlserver.cache_mgr + sqlserver.cache_mgr.all.cache_hit_ratio PMID: 79.0.81 + sqlserver.cache_mgr.cache.cache_hit_ratio PMID: 79.0.82 + +e. Metric semantics ... you'll need a complete metricdesc[i] + initializer from pmda.c ... pick one that is similar, copy, paste + at the end of the table initialization and update PMDA_PMID(0,y) + with x+1 from step a. + + To help with "similar" here are some examples: + + kernel.all.cpu.user - unsigned 64-bit counter in units of microseconds + + network.interface.in.bytes - unsigned 32-bit counter in units of + bytes with one value per network interface + + mem.available - unsigned 64-bit instantaneous value in units of + Mbytes + + sqlserver.buf_mgr.cache_hit_ratio - floating point value + representing a cache hit ratio, returned in the range 0.0 to + 1.0 + + Be careful with '\' in the Pdh patterns. Replace them with '\\' in the + string initializer used here. + + My new metrics are cache hit ratios + + /* sqlserver.cache_mgr.all.cache_hit_ratio */ + { { PMDA_PMID(0,81), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(_Total)\\Cache Hit Ratio" + }, + /* sqlserver.cache_mgr.cache.cache_hit_ratio */ + { { PMDA_PMID(0,82), PM_TYPE_FLOAT, SQL_CACHE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(*/*#*)\\Cache Hit Ratio" + }, + +f. If you've added a new instance domain (SQL_CACHE_INDOM in the case + above, this needs to be handled): + i. add a new header #define line (use the next ordinal number), e.g. + #define SQL_CACHE_INDOM 5 + ii. add a new entry to indomtab[] in pmda.c, e.g. + { SQL_CACHE_INDOM, 0, NULL }, + iii. add a new entry to indomtab[] in instance.c, e.g. + SQL_CACHE_INDOM, + iv. add new code in the switch of check_instance() in instance.c + to parse the Pdh instance name to extract a PCP instance name + and assign an instance number ... it is strongly suggested + that you study the ones already there, steal and modify the + one that is closest to your new instance + +g. Make and upgrade (assuming you're on the target machine and not + interested in packaging) + + $ make + $ su + # /etc/pcp stop + # make install + # cd $PCP_VAR_DIR/pmdas/windows + # ./Install + diff --git a/src/pmdas/windows/error.c b/src/pmdas/windows/error.c new file mode 100644 index 0000000..9961d78 --- /dev/null +++ b/src/pmdas/windows/error.c @@ -0,0 +1,130 @@ +/* + * Error code -> message map comes from + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/base/pdh_error_codes.asp + * + * Copyright (c) 2008 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "hypnotoad.h" + +static struct { + int code; + char *msg; +} errtab[] = { + { PDH_CSTATUS_VALID_DATA, "The returned data is valid." }, + { PDH_CSTATUS_NEW_DATA, "The return data value is valid and different from the last sample." }, + { PDH_CSTATUS_NO_MACHINE, "Unable to connect to specified machine or machine is off line." }, + { PDH_CSTATUS_NO_INSTANCE, "The specified instance is not present." }, + { PDH_MORE_DATA, "There is more data to return than would fit in the supplied buffer. Allocate a larger buffer and call the function again." }, + { PDH_CSTATUS_ITEM_NOT_VALIDATED, "The data item has been added to the query but has not been validated nor accessed. No other status information on this data item is available." }, + { PDH_RETRY, "The selected operation should be retried." }, + { PDH_NO_DATA, "No data to return." }, + { PDH_CALC_NEGATIVE_DENOMINATOR, "A counter with a negative denominator value was detected." }, + { PDH_CALC_NEGATIVE_TIMEBASE, "A counter with a negative time base value was detected." }, + { PDH_CALC_NEGATIVE_VALUE, "A counter with a negative value was detected." }, + { PDH_DIALOG_CANCELLED, "The user canceled the dialog box." }, + { PDH_END_OF_LOG_FILE, "The end of the log file was reached." }, + { PDH_ASYNC_QUERY_TIMEOUT, "Time out while waiting for asynchronous counter collection thread to end." }, + { PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE, "Cannot change set default real-time data source. There are real-time query sessions collecting counter data." }, + { PDH_CSTATUS_NO_OBJECT, "The specified object is not found on the system." }, + { PDH_CSTATUS_NO_COUNTER, "The specified counter could not be found." }, + { PDH_CSTATUS_INVALID_DATA, "The returned data is not valid." }, + { PDH_MEMORY_ALLOCATION_FAILURE, "A PDH function could not allocate enough temporary memory to complete the operation. Close some applications or extend the page file and retry the function." }, + { PDH_INVALID_HANDLE, "The handle is not a valid PDH object." }, + { PDH_INVALID_ARGUMENT, "A required argument is missing or incorrect." }, + { PDH_FUNCTION_NOT_FOUND, "Unable to find the specified function." }, + { PDH_CSTATUS_NO_COUNTERNAME, "No counter was specified." }, + { PDH_CSTATUS_BAD_COUNTERNAME, "Unable to parse the counter path. Check the format and syntax of the specified path." }, + { PDH_INVALID_BUFFER, "The buffer passed by the caller is invalid." }, + { PDH_INSUFFICIENT_BUFFER, "The requested data is larger than the buffer supplied. Unable to return the requested data." }, + { PDH_CANNOT_CONNECT_MACHINE, "Unable to connect to the requested machine." }, + { PDH_INVALID_PATH, "The specified counter path could not be interpreted." }, + { PDH_INVALID_INSTANCE, "The instance name could not be read from the specified counter path." }, + { PDH_INVALID_DATA, "The data is not valid." }, + { PDH_NO_DIALOG_DATA, "The dialog box data block was missing or invalid." }, + { PDH_CANNOT_READ_NAME_STRINGS, "Unable to read the counter and/or explain text from the specified machine." }, + { PDH_LOG_FILE_CREATE_ERROR, "Unable to create the specified log file." }, + { PDH_LOG_FILE_OPEN_ERROR, "Unable to open the specified log file." }, + { PDH_LOG_TYPE_NOT_FOUND, "The specified log file type has not been installed on this system." }, + { PDH_NO_MORE_DATA, "No more data is available." }, + { PDH_ENTRY_NOT_IN_LOG_FILE, "The specified record was not found in the log file." }, + { PDH_DATA_SOURCE_IS_LOG_FILE, "The specified data source is a log file." }, + { PDH_DATA_SOURCE_IS_REAL_TIME, "The specified data source is the current activity." }, + { PDH_UNABLE_READ_LOG_HEADER, "The log file header could not be read." }, + { PDH_FILE_NOT_FOUND, "Unable to find the specified file." }, + { PDH_FILE_ALREADY_EXISTS, "There is already a file with the specified file name." }, + { PDH_NOT_IMPLEMENTED, "The function referenced has not been implemented." }, + { PDH_STRING_NOT_FOUND, "Unable to find the specified string in the list of performance name and explain text strings." }, + { PDH_UNABLE_MAP_NAME_FILES, "Unable to map to the performance counter name data files. The data will be read from the registry and stored locally." }, + { PDH_UNKNOWN_LOG_FORMAT, "The format of the specified log file is not recognized by the PDH DLL." }, + { PDH_UNKNOWN_LOGSVC_COMMAND, "The specified Log Service command value is not recognized." }, + { PDH_LOGSVC_QUERY_NOT_FOUND, "The specified Query from the Log Service could not be found or could not be opened." }, + { PDH_LOGSVC_NOT_OPENED, "The Performance Data Log Service key could not be opened. This may be due to insufficient privilege or because the service has not been installed." }, + { PDH_WBEM_ERROR, "An error occurred while accessing the WBEM data store." }, + { PDH_ACCESS_DENIED, "Unable to access the desired machine or service. Check the permissions and authentication of the log service or the interactive user session against those on the machine or service being monitored." }, + { PDH_LOG_FILE_TOO_SMALL, "The maximum log file size specified is too small to log the selected counters. No data will be recorded in this log file. Specify a smaller set of counters to log or a larger file size and retry this call." }, + { PDH_INVALID_DATASOURCE, "Cannot connect to ODBC DataSource Name." }, + { PDH_INVALID_SQLDB, "SQL Database does not contain a valid set of tables for Perfmon; use PdhCreateSQLTables." }, + { PDH_NO_COUNTERS, "No counters were found for this Perfmon SQL Log Set." }, + { PDH_SQL_ALLOC_FAILED, "Call to SQLAllocStmt failed with %1." }, + { PDH_SQL_ALLOCCON_FAILED, "Call to SQLAllocConnect failed with %1." }, + { PDH_SQL_EXEC_DIRECT_FAILED, "Call to SQLExecDirect failed with %1." }, + { PDH_SQL_FETCH_FAILED, "Call to SQLFetch failed with %1." }, + { PDH_SQL_ROWCOUNT_FAILED, "Call to SQLRowCount failed with %1." }, + { PDH_SQL_MORE_RESULTS_FAILED, "Call to SQLMoreResults failed with %1." }, + { PDH_SQL_CONNECT_FAILED, "Call to SQLConnect failed with %1." }, + { PDH_SQL_BIND_FAILED, "Call to SQLBindCol failed with %1." }, + { PDH_CANNOT_CONNECT_WMI_SERVER, "Unable to connect to the WMI server on requested machine." }, + { PDH_PLA_COLLECTION_ALREADY_RUNNING, "Collection %1s is already running." }, + { PDH_PLA_ERROR_SCHEDULE_OVERLAP, "The specified start time is after the end time." }, + { PDH_PLA_COLLECTION_NOT_FOUND, "Collection %1 does not exist." }, + { PDH_PLA_ERROR_SCHEDULE_ELAPSED, "The specified end time has already elapsed." }, + { PDH_PLA_ERROR_NOSTART, "Collection %1 did not start check the application event log for any errors." }, + { PDH_PLA_ERROR_ALREADY_EXISTS, "Collection %1 already exists." }, + { PDH_PLA_ERROR_TYPE_MISMATCH, "There is a mismatch in the settings type." }, + { PDH_PLA_ERROR_FILEPATH, "The information specified does not resolve to a valid path name." }, + { PDH_PLA_SERVICE_ERROR, "The 'Performance Logs & Alerts' service did not respond." }, + { PDH_PLA_VALIDATION_ERROR, "The information passed is not valid." }, + { PDH_PLA_VALIDATION_WARNING, "The information passed is not valid." }, + { PDH_PLA_ERROR_NAME_TOO_LONG, "The name supplied is too long." }, + { PDH_INVALID_SQL_LOG_FORMAT, "SQL log format is incorrect. Correct format is 'SQL:!'." }, + { PDH_COUNTER_ALREADY_IN_QUERY, "Performance counter in PdhAddCounter call has already been added in the performance query. This counter is ignored." }, + { PDH_BINARY_LOG_CORRUPT, "Unable to read counter information and data from input binary log files." }, + { PDH_LOG_SAMPLE_TOO_SMALL, "At least one of the input binary log files contain fewer than two data samples." }, + { PDH_OS_LATER_VERSION, "The version of the operating system on the computer named %1 is later than that on the local computer. This operation is not available from the local computer." }, + { PDH_OS_EARLIER_VERSION, "supports %2 or later. Check the operating system version on the computer named %3." }, + { PDH_INCORRECT_APPEND_TIME, "The output file must contain earlier data than the file to be appended." }, + { PDH_UNMATCHED_APPEND_COUNTER, "Both files must have identical counters in order to append." }, + { PDH_SQL_ALTER_DETAIL_FAILED, "Cannot alter CounterDetail table layout in SQL database." }, + { PDH_QUERY_PERF_DATA_TIMEOUT, "System is busy. Timeout when collecting counter data. Please retry later or increase the CollectTime registry value." } +}; + +static int sz_errtab = sizeof(errtab) / sizeof(errtab[0]); +static char *buf = "eh? 0x........"; + +char *pdherrstr(int code) +{ + int i; + + for (i = 0; i < sz_errtab; i++) { + if (code == errtab[i].code) + return errtab[i].msg; + } + sprintf(buf, "eh? 0x%08x", code); + return buf; +} diff --git a/src/pmdas/windows/fetch.c b/src/pmdas/windows/fetch.c new file mode 100644 index 0000000..cc43955 --- /dev/null +++ b/src/pmdas/windows/fetch.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2008-2010 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "hypnotoad.h" + +MEMORYSTATUSEX windows_memstat; + +void +windows_fetch_memstat(void) +{ + ZeroMemory(&windows_memstat, sizeof(MEMORYSTATUSEX)); + windows_memstat.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&windows_memstat); +} + +/* + * Instantiate a value for a single metric-instance pair + */ +int +windows_collect_metric(pdh_metric_t *mp, LPSTR pat, pdh_value_t *vp) +{ + PDH_STATUS pdhsts; + PDH_HQUERY queryhdl = NULL; + PDH_HCOUNTER counthdl = NULL; + int sts = -1; + + if (mp->flags & M_NOVALUES) + return sts; + + pdhsts = PdhOpenQueryA(NULL, 0, &queryhdl); + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "windows_open: PdhOpenQueryA failed: %s\n", + pdherrstr(pdhsts)); + return sts; + } + + pdhsts = PdhAddCounterA(queryhdl, pat, vp->inst, &counthdl); + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "windows_open: Warning: PdhAddCounterA " + "@ pmid=%s pat=\"%s\": %s\n", + pmIDStr(mp->desc.pmid), pat, pdherrstr(pdhsts)); + PdhCloseQuery(queryhdl); + return sts; + } + + pdhsts = PdhCollectQueryData(queryhdl); + if (pdhsts != ERROR_SUCCESS) { + if ((vp->flags & V_ERROR_SEEN) == 0) { + __pmNotifyErr(LOG_ERR, "pdh_fetch: Error: PdhCollectQueryData " + "failed for metric %s pat %s: %s\n", + pmIDStr(mp->desc.pmid), pat, pdherrstr(pdhsts)); + vp->flags |= V_ERROR_SEEN; + } + } else if ((mp->ctype == PERF_ELAPSED_TIME) || + mp->ctype == PERF_LARGE_RAW_FRACTION) { + PDH_FMT_COUNTERVALUE fmt; + DWORD type; + + if (mp->ctype == PERF_ELAPSED_TIME) + type = PDH_FMT_LARGE; + else /* PERF_LARGE_RAW_FRACTION */ + type = PDH_FMT_DOUBLE; + + pdhsts = PdhGetFormattedCounterValue(counthdl, type, NULL, &fmt); + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "Error: PdhGetFormattedCounterValue " + "failed for metric %s inst %d: %s\n", + pmIDStr(mp->desc.pmid), vp->inst, pdherrstr(pdhsts)); + vp->flags = V_NONE; /* no values for you! */ + } else if (mp->ctype == PERF_ELAPSED_TIME) { + vp->atom.ull = fmt.largeValue; + sts = 0; + } else { /* PERF_LARGE_RAW_FRACTION */ + vp->atom.d = fmt.doubleValue; + sts = 0; + } + } else { + PDH_RAW_COUNTER raw; + + pdhsts = PdhGetRawCounterValue(counthdl, NULL, &raw); + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "pdh_fetch: Error: PdhGetRawCounterValue " + "failed for metric %s inst %d: %s\n", + pmIDStr(mp->desc.pmid), vp->inst, pdherrstr(pdhsts)); + vp->flags = V_NONE; /* no values for you! */ + } else { + switch (mp->ctype) { + case PERF_COUNTER_COUNTER: + case PERF_COUNTER_RAWCOUNT: + /* these counters are only 32-bit */ + vp->atom.ul = (__uint32_t)raw.FirstValue; + break; + + case PERF_100NSEC_TIMER: + case PERF_PRECISION_100NS_TIMER: + /* convert 100nsec units to usec */ + vp->atom.ull = raw.FirstValue / 10; + break; + + case PERF_RAW_FRACTION: + /* v1 / v2 as percentage */ + vp->atom.f = (float)raw.FirstValue / raw.SecondValue; + break; + + case PERF_COUNTER_BULK_COUNT: + case PERF_COUNTER_LARGE_RAWCOUNT: + default: + vp->atom.ull = raw.FirstValue; + } + sts = 0; + } + } + PdhRemoveCounter(counthdl); + PdhCloseQuery(queryhdl); + return sts; +} + +void +windows_collect_callback(pdh_metric_t *pmp, LPTSTR pat, pdh_value_t *pvp) +{ + windows_verify_callback(pmp, pat, pvp); + + if (!(pvp->flags & V_COLLECTED)) + if (windows_collect_metric(pmp, pat, pvp) == 0) + pvp->flags |= V_COLLECTED; +} + +/* + * Called before each PMDA fetch ... force value refreshes for + * requested metrics here; and special case any derived metrics. + */ +void +windows_fetch_refresh(int numpmid, pmID pmidlist[], pmdaExt *pmda) +{ + int i, j, extra_filesys = 0, extra_memstat = 0; + int extra_hinv_ncpu = -1, extra_hinv_ndisk = -1; + int extra_network = -1; + + for (i = 0; i < NUMINDOMS; i++) + windows_indom_reset[i] = 0; + + for (i = 0; i < metricdesc_sz; i++) + for (j = 0; j < metricdesc[i].num_vals; j++) + metricdesc[i].vals[j].flags = V_NONE; + + for (i = 0; i < numpmid; i++) { + __pmID_int *pmidp = (__pmID_int *)&pmidlist[i]; + int cluster = pmidp->cluster; + int item = pmidp->item; + + if (cluster == 1) + extra_memstat = 1; + else if (cluster != 0) + continue; + else if (item == 106) + extra_memstat = 1; + else if (item == 107 && extra_hinv_ncpu == -1) + extra_hinv_ncpu = 1; + else if (item == 108 && extra_hinv_ndisk == -1) + extra_hinv_ndisk = 1; + else if (item >= 117 && item <= 119) + extra_filesys = 1; + else if (item >= 236 && item <= 237 && extra_network == -1) + extra_network = 1; + else { + if (item >= 4 && item <= 7) + extra_hinv_ncpu = 0; + else if ((item >= 21 && item <= 26) || item == 68 || + (item >= 217 && item <= 219) || item == 101 || + (item >= 226 && item <= 231) || item == 133) + extra_hinv_ndisk = 0; + else if (item == 235) + extra_network = 0; + + windows_visit_metric(&metricdesc[item], windows_collect_callback); + } + } + + if (extra_memstat) + windows_fetch_memstat(); + if (extra_hinv_ncpu == 1) + windows_visit_metric(&metricdesc[4], NULL); + if (extra_hinv_ndisk == 1) + windows_visit_metric(&metricdesc[21], NULL); + if (extra_filesys) { + windows_visit_metric(&metricdesc[120], windows_collect_callback); + windows_visit_metric(&metricdesc[121], windows_collect_callback); + } + if (extra_network == 1) + windows_visit_metric(&metricdesc[235], windows_collect_callback); + + for (i = 0; i < NUMINDOMS; i++) { + /* Do we want to persist this instance domain to disk? */ + if (windows_indom_reset[i] && windows_indom_fixed(i)) + pmdaCacheOp(INDOM(pmda->e_domain, i), PMDA_CACHE_SAVE); + } +} diff --git a/src/pmdas/windows/help b/src/pmdas/windows/help new file mode 100644 index 0000000..b58a1ea --- /dev/null +++ b/src/pmdas/windows/help @@ -0,0 +1,65 @@ +# +# Copyright (c) 2010 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Windows PMDA help file in the ASCII format. Only metrics which are +# not using PDH regular expressions for extraction should be contained +# here - the rest have their help text auto-generated by Windows PDH. +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# +@ kernel.uname.release release level of the running kernel +@ kernel.uname.version version level (build number) and build date of the running kernel +@ kernel.uname.sysname name of the implementation of the operating system +@ kernel.uname.machine name of the hardware type the system is running on +@ kernel.uname.nodename host name of this node on the network +@ kernel.uname.distro Windows distribution name +The Windows distribution name, from the GetVersionEx Win32 API. + +@ network.interface.speed interface speed in megabytes per second +The linespeed on the network interface, as reported by the kernel, +scaled from Megabits/second to Megabytes/second. +See also network.interface.baudrate for the bytes/second value. + +@ network.interface.baudrate interface speed in bytes per second +The linespeed on the network interface, as reported by the kernel, +scaled from bits/second and divided by 8 to convert to bytes/second. +See also network.interface.speed for the Megabytes/second value. + +@ pmda.uname identity and type of current system +Identity and type of current system. + +See also the kernel.uname.* metrics + +@ pmda.version build version of Windows PMDA + +@ hinv.physmem total system memory metric +@ hinv.pagesize Memory page size +The memory page size of the running kernel in bytes. +@ hinv.ncpu number of CPUs in the system +@ hinv.ndisk number of disks in the syste (excluding floppy drives) +@ hinv.nfilesys number of (local) file systems currently mounted + +@ filesys.capacity Total capacity of mounted filesystem (Kbytes) +@ filesys.used Total space used on mounted filesystem (Kbytes) +@ filesys.free Total space free on mounted filesystem (Kbytes) diff --git a/src/pmdas/windows/helptext.c b/src/pmdas/windows/helptext.c new file mode 100644 index 0000000..d1876e1 --- /dev/null +++ b/src/pmdas/windows/helptext.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008-2009 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include "hypnotoad.h" + +static char *text; /* filled in by iterator callback routine */ +static char texts[MAX_M_TEXT_LEN]; /* static callback buffer */ + +/* + * Replace backslashes in the help string returned from Pdh APIs. + * Everything done "in-place" so no change to size of the string. + */ +static char * +windows_fmt(char *text) +{ + char *p; + int n; + + for (p = text, n = 0; p && *p != '\0'; p++, n++) { + if (!isprint((int)*p)) /* toss any dodgey characters */ + *p = '?'; + else if (*p == '\r') /* remove Windows line ending */ + *p = '\n'; + if (n < 70 || !isspace((int)*p)) /* very simple line wrapping */ + continue; + *p = '\n'; + n = 0; + } + return text; +} + +static void +windows_helptext_metric(pdh_metric_t *mp, PDH_COUNTER_INFO_A *infop) +{ + text = infop->szExplainText; +} + +static void +windows_helptext_callback(pdh_metric_t *pmp, LPSTR pat, pdh_value_t *pvp) +{ + windows_inform_metric(pmp, pat, pvp, TRUE, windows_helptext_metric); +} + +int +windows_help(int ident, int type, char **buf, pmdaExt *pmda) +{ + pmID pmid = (pmID)ident; + int i; + + if ((type & PM_TEXT_PMID) != PM_TEXT_PMID) + return pmdaText(ident, type, buf, pmda); + + for (i = 0; i < metricdesc_sz; i++) + if (pmid == metricdesc[i].desc.pmid) + break; + if (i == metricdesc_sz) + return PM_ERR_PMID; + + if (type & PM_TEXT_ONELINE) { + if (metricdesc[i].pat[0] == '\0') + return pmdaText(ident, type, buf, pmda); + *buf = windows_fmt(strncpy(texts, &metricdesc[i].pat[0], sizeof(texts))); + } else { + text = NULL; + windows_visit_metric(&metricdesc[i], windows_helptext_callback); + if (!text) + return pmdaText(ident, type, buf, pmda); + *buf = windows_fmt(strncpy(texts, text, sizeof(texts))); + } + return 0; +} diff --git a/src/pmdas/windows/hypnotoad.h b/src/pmdas/windows/hypnotoad.h new file mode 100644 index 0000000..ea75430 --- /dev/null +++ b/src/pmdas/windows/hypnotoad.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008-2010 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef HYPNOTOAD_H +#define HYPNOTOAD_H + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "pdh.h" +#include "pdhmsg.h" +#include "domain.h" + +#define MAX_M_PATH_LEN 80 /* pattern passed to PdhExpandCounterPath */ +#define MAX_M_TEXT_LEN 512 /* longest long-text string that we allow */ +#define INDOM(x,y) (((x)<<22)|(y)) /* pmdaCache interfaces use indom */ + +enum { + DISK_INDOM, + CPU_INDOM, + NETIF_INDOM, + FILESYS_INDOM, + SQL_LOCK_INDOM, + SQL_CACHE_INDOM, + SQL_DB_INDOM, + PROCESS_INDOM, + THREAD_INDOM, + SQL_USER_INDOM, + NUMINDOMS +}; + +typedef enum { + V_NONE, + V_ERROR_SEEN = 0x1, + V_COLLECTED = 0x2, /* if PdhGetRawCounterValue was successful */ +} pdh_valueflags_t; + +typedef struct { + int inst; /* PM_IN_NULL or instance identifier */ + pdh_valueflags_t flags; + pmAtomValue atom; +} pdh_value_t; + +typedef enum { + M_NONE, + M_EXPANDED = 0x1, /* pattern has been expanded */ + M_REDO = 0x2, /* redo pattern expansion on each fetch */ + M_NOVALUES = 0x4, /* setup failed, don't bother with the fetch */ + M_OPTIONAL = 0x8, /* optional component, no values is expected */ + M_VERIFIED = 0x10, /* has this metrics semantics been checked */ + M_AUTO64 = 0x20, /* allow auto-modification on 64/32bit type */ +} pdh_metricflag_t; + +typedef struct { + pmDesc desc; /* metric descriptor */ + pdh_metricflag_t flags; /* state of this metric */ + int ctype; /* PDH counter type */ + int num_alloc; /* high water allocation mark */ + int num_vals; /* one or more metric values */ + pdh_value_t *vals; + char pat[MAX_M_PATH_LEN]; /* for PdhExpandCounterPath */ +} pdh_metric_t; + +extern pdh_metric_t metricdesc[]; +extern int metricdesc_sz; + +extern char *windows_uname; +extern char *windows_build; +extern char *windows_machine; +extern int windows_indom_setup[]; +extern int windows_indom_reset[]; +extern unsigned long windows_pagesize; +extern MEMORYSTATUSEX windows_memstat; +extern void windows_fetch_memstat(void); + +extern void windows_open(int); +extern int windows_indom_fixed(int); +extern char *pdherrstr(int); + +typedef void (*pdh_metric_inform_t)(pdh_metric_t *, PDH_COUNTER_INFO_A *); +typedef void (*pdh_metric_visitor_t)(pdh_metric_t *, LPSTR, pdh_value_t *); +extern int windows_visit_metric(pdh_metric_t *, pdh_metric_visitor_t); +extern int windows_inform_metric(pdh_metric_t *, LPTSTR, pdh_value_t *, + BOOLEAN, pdh_metric_inform_t); + +extern void windows_instance_refresh(pmInDom); +extern int windows_lookup_instance(char *, pdh_metric_t *); +extern void windows_fetch_refresh(int numpmid, pmID pmidlist[], pmdaExt *); +extern void windows_verify_callback(pdh_metric_t *, LPSTR, pdh_value_t *); + +extern int windows_help(int, int, char **, pmdaExt *); + +#endif /* HYPNOTOAD_H */ diff --git a/src/pmdas/windows/instance.c b/src/pmdas/windows/instance.c new file mode 100644 index 0000000..fbbb083 --- /dev/null +++ b/src/pmdas/windows/instance.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2008-2010 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Parts of this file contributed by Ken McDonell + * (kenj At internode DoT on DoT net) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "hypnotoad.h" +#include + +int +windows_indom_fixed(int serial) +{ + return (serial != PROCESS_INDOM && serial != THREAD_INDOM); +} + +void +windows_instance_refresh(pmInDom indom) +{ + int i, index, setup; + + index = pmInDom_serial(indom); + windows_indom_reset[index] = 0; + setup = windows_indom_setup[index]; + + for (i = 0; i < metricdesc_sz; i++) { + pdh_metric_t *mp = &metricdesc[i]; + + if (indom != mp->desc.indom || mp->pat[0] != '\\') + continue; + if (!setup || (mp->flags & M_REDO)) + windows_visit_metric(mp, NULL); + break; + } + + /* Do we want to persist this instance domain to disk? */ + if (windows_indom_reset[index] && windows_indom_fixed(index)) + pmdaCacheOp(indom, PMDA_CACHE_SAVE); +} + +int +windows_lookup_instance(char *path, pdh_metric_t *mp) +{ + __pmInDom_int *ip; + static void *seen = (void *)0xfeedbabe; + void *sp; + char *p, *q, *name = NULL; + int sts, ok = 0; + + if (mp->desc.indom == PM_INDOM_NULL) + return PM_IN_NULL; + + ip = (__pmInDom_int *)&mp->desc.indom; + switch (ip->serial) { + /* + * Examples: + * \\WINBUILD\PhysicalDisk(0 C:)\Disk Reads/sec + * \\SOMEHOST\PhysicalDisk(0 C: D: E:)\Disk Transfers/sec + * \\WINBUILD\PhysicalDisk(_Total)\Disk Write Bytes/sec + */ + case DISK_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals get enumerated in the per disk path + * expansion, just skip 'em here + */ + return -1; + } + while (isascii((int)*p) && isdigit((int)*p)) p++; + if (*p == ' ') { + p++; + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: " + "Error: DISK_INDOM malloc[%d] failed " + "path=%s\n", q - p + 1, path); + return -1; + } + /* + * If more than one drive letter maps to the same + * logical disk (e.g. mirrored root),, the name + * contains spaces, e.g. + * "C: D:" ... replace ' ' by '_' to play by the + * PCP rules for instance names + */ + for (p = name; *p; p++) { + if (*p == ' ') *p = '_'; + } + } + } + } + /* + * expecting something like ...\PhysicalDisk(N name)... + * we will just have to ignore this one! (might be due + * to: http://support.microsoft.com/kb/974878 - fail as + * entries like "17" and "17#1" are not useable anyway) + */ + if (!ok) { + if (pmDebug & DBG_TRACE_LIBPMDA) + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized disk instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Examples: + * \\WINBUILD\Processor(0)\% User Time + * \\WINBUILD\Processor(_Total)\Interrupts/sec + */ + case CPU_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals get enumerated in the per cpu path + * expansion, just skip 'em here + */ + return -1; + } + int inst = atoi(p); + name = (char *)malloc(8); // "cpuNNNN" + if (name != NULL) + sprintf(name, "cpu%d", inst); + ok = 1; + } + /* + * expecting something like ...\Processor(N)... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized cpu instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Examples: + * \\WINNT\Network Interface(MS TCP Loopback interface)\Bytes Total/sec + */ + case NETIF_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed for NETIF_INDOM " + "path=%s\n", q - p + 1, path); + return -1; + } + /* + * The network interface names have many spaces and are + * not unique up to the first space by any means. So, + * replace ' 's to play by the PCP instance name rules. + */ + for (p = name; *p; p++) { + if (*p == ' ') *p = '_'; + } + } + } + /* + * expecting something like ...\Network Interface(...)... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized network interface instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Examples: + * \\TOWER\LogicalDisk(C:)\% Free Space + */ + case FILESYS_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals value makes no semantic sense, + * just skip it here + */ + return -1; + } + while (isascii((int)*p) && isdigit((int)*p)) + p++; + if (*p == ' ') + p++; + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed for LDISK_INDOM path=%s\n", + q - p + 1, path); + return -1; + } + } + } + /* + * expecting something like ...\LogicalDisk(C:)... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized logical disk instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * SQLServer instance domains all have similar syntax + * + * Examples: + * \\TOWER\SQLServer:Locks(Table)\Average Wait Time (ms) + * \\TOWER\SQLServer:Cache Manager(Cursors)\Cache Hit Ratio + * \\TOWER\SQLServer:Databases(ACONEX_SYS)\Transactions/sec + */ + case SQL_LOCK_INDOM: + case SQL_CACHE_INDOM: + case SQL_DB_INDOM: + case SQL_USER_INDOM: + p = strchr(path, '('); // skip hostname and metric name + if (p != NULL) { + p++; + if (strncmp(p, "_Total)", 7) == 0) { + /* + * The totals are done as independent metrics, + * just skip them here + */ + return -1; + } + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed, SQL_INDOM path=%s\n", + q - p + 1, path); + return -1; + } + + /* + * The user counter names have many spaces and are + * not unique up to the first space by any means. So, + * replace ' 's to play by the PCP instance name rules. + */ + if (ip->serial == SQL_USER_INDOM) { + for (p = name; *p; p++) + if (*p == ' ') *p = '_'; + } + } + } + /* + * expecting something like ... \SQLServer:...(...)\... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized SQLServer instance: %s\n", path); + free(name); + return -1; + } + break; + + /* + * Per-process and per-thread instance domain + * + * Examples: + * \\TOWER\Process(svchost#6)\% Processor Time + * \\TOWER\Thread(svchost/1#1)\% Processor Time + * \\TOWER\Thread(Idle/0)\ID Process + * \\TOWER\Thread(Idle/0)\ID Thread + */ + case PROCESS_INDOM: + case THREAD_INDOM: + p = strchr(path, '('); // skip hostname and Process/Thread + if (p != NULL) { + p++; + if ((strncmp(p, "_Total)", 7) == 0) || + (strncmp(p, "_Total/", 7) == 0)) { + /* + * The totals are done as independent metrics, + * just skip them here + */ + return -1; + } + q = strchr(p, ')'); + if (q != NULL) { + name = (char *)malloc(q - p + 1); + if (name != NULL) { + strncpy(name, p, q - p); + name[q - p] = '\0'; + ok = 1; + } + else { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "malloc[%d] failed, process/thread path=%s\n", + q - p + 1, path); + return -1; + } + } + } + /* + * expecting something like ... \Process(...)\... + * don't know what to do with this one! + */ + if (!ok) { + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "unrecognized process/thread name: %s\n", path); + free(name); + return -1; + } + break; + + default: + __pmNotifyErr(LOG_ERR, "windows_check_instance: Error: " + "pmInDom %s is unknown for metric %s\n", + pmInDomStr(mp->desc.indom), pmIDStr(mp->desc.pmid)); + return -1; + } + + sts = pmdaCacheLookupName(mp->desc.indom, name, &ok, &sp); + if (sts != PMDA_CACHE_ACTIVE) { + if (sp != seen) /* new instance, never seen before, mark it */ + windows_indom_reset[pmInDom_serial(mp->desc.indom)] = 1; + ok = pmdaCacheStore(mp->desc.indom, PMDA_CACHE_ADD, name, seen); + } + free(name); + return ok; +} diff --git a/src/pmdas/windows/open.c b/src/pmdas/windows/open.c new file mode 100644 index 0000000..65be00d --- /dev/null +++ b/src/pmdas/windows/open.c @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2008-2010 Aconex. All Rights Reserved. + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + * Parts of this file contributed by Ken McDonell + * (kenj At internode DoT on DoT net) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "hypnotoad.h" +#include + +#define roundup(x,y) ((((x) + ((y) - 1)) / (y)) * (y)) + +char *windows_uname; +char *windows_build; +char *windows_machine; +unsigned long windows_pagesize; +int windows_indom_setup[NUMINDOMS]; /* initial setup done on instance */ +int windows_indom_reset[NUMINDOMS]; /* instances changed on refresh */ + +/* + * This block of functionality is required to map counter types from + * their Windows semantics to equivalent PCP semantics. + */ + +static struct { + int type; + char *desc; +} ctypetab[] = { + { PERF_100NSEC_MULTI_TIMER, "PERF_100NSEC_MULTI_TIMER" }, + { PERF_100NSEC_MULTI_TIMER_INV, "PERF_100NSEC_MULTI_TIMER_INV" }, + { PERF_100NSEC_TIMER, "PERF_100NSEC_TIMER" }, + { PERF_100NSEC_TIMER_INV, "PERF_100NSEC_TIMER_INV" }, + { PERF_AVERAGE_BASE, "PERF_AVERAGE_BASE" }, + { PERF_AVERAGE_BULK, "PERF_AVERAGE_BULK" }, + { PERF_AVERAGE_TIMER, "PERF_AVERAGE_TIMER" }, + { PERF_COUNTER_100NS_QUEUELEN_TYPE, "PERF_COUNTER_100NS_QUEUELEN_TYPE" }, + { PERF_COUNTER_BULK_COUNT, "PERF_COUNTER_BULK_COUNT" }, + { PERF_COUNTER_COUNTER, "PERF_COUNTER_COUNTER" }, + { PERF_COUNTER_DELTA, "PERF_COUNTER_DELTA" }, + { PERF_COUNTER_LARGE_DELTA, "PERF_COUNTER_LARGE_DELTA" }, + { PERF_COUNTER_LARGE_QUEUELEN_TYPE, "PERF_COUNTER_LARGE_QUEUELEN_TYPE" }, + { PERF_COUNTER_LARGE_RAWCOUNT, "PERF_COUNTER_LARGE_RAWCOUNT" }, + { PERF_COUNTER_LARGE_RAWCOUNT_HEX, "PERF_COUNTER_LARGE_RAWCOUNT_HEX" }, + { PERF_COUNTER_MULTI_BASE, "PERF_COUNTER_MULTI_BASE" }, + { PERF_COUNTER_MULTI_TIMER, "PERF_COUNTER_MULTI_TIMER" }, + { PERF_COUNTER_MULTI_TIMER_INV, "PERF_COUNTER_MULTI_TIMER_INV" }, + { PERF_COUNTER_NODATA, "PERF_COUNTER_NODATA" }, + { PERF_COUNTER_QUEUELEN_TYPE, "PERF_COUNTER_QUEUELEN_TYPE" }, + { PERF_COUNTER_RAWCOUNT, "PERF_COUNTER_RAWCOUNT" }, + { PERF_COUNTER_RAWCOUNT_HEX, "PERF_COUNTER_RAWCOUNT_HEX" }, + { PERF_COUNTER_TEXT, "PERF_COUNTER_TEXT" }, + { PERF_COUNTER_TIMER, "PERF_COUNTER_TIMER" }, + { PERF_COUNTER_TIMER_INV, "PERF_COUNTER_TIMER_INV" }, + { PERF_ELAPSED_TIME, "PERF_ELAPSED_TIME" }, + { PERF_LARGE_RAW_BASE, "PERF_LARGE_RAW_BASE" }, + { PERF_OBJ_TIME_TIMER, "PERF_OBJ_TIME_TIMER" }, + { PERF_PRECISION_100NS_TIMER, "PERF_PRECISION_100NS_TIMER" }, + { PERF_PRECISION_OBJECT_TIMER, "PERF_PRECISION_OBJECT_TIMER" }, + { PERF_PRECISION_SYSTEM_TIMER, "PERF_PRECISION_SYSTEM_TIMER" }, + { PERF_RAW_BASE, "PERF_RAW_BASE" }, + { PERF_RAW_FRACTION, "PERF_RAW_FRACTION" }, + { PERF_LARGE_RAW_FRACTION, "PERF_LARGE_RAW_FRACTION" }, + { PERF_SAMPLE_BASE, "PERF_SAMPLE_BASE" }, + { PERF_SAMPLE_COUNTER, "PERF_SAMPLE_COUNTER" }, + { PERF_SAMPLE_FRACTION, "PERF_SAMPLE_FRACTION" } +}; + +static int ctypetab_sz = sizeof(ctypetab) / sizeof(ctypetab[0]); + +static char * +decode_ctype(DWORD ctype) +{ + static char unknown[20]; + int i; + + for (i = 0; i < ctypetab_sz; i++) + if (ctype == ctypetab[i].type) + return ctypetab[i].desc; + sprintf(unknown, "0x%08x unknown", (int)ctype); + return unknown; +} + +static char * +string_append(char *name, char *suff) +{ + if (name == NULL) { + name = (char *)strdup(suff); + } + else { + name = (char *)realloc(name, strlen(name)+strlen(suff)+1); + strcat(name, suff); + } + return name; +} + +static char * +_semstr(int sem) +{ + static char msg[20]; + if (sem == PM_SEM_COUNTER) + return "COUNTER"; + else if (sem == PM_SEM_INSTANT) + return "INSTANT"; + else if (sem == PM_SEM_DISCRETE) + return "DISCRETE"; + else { + sprintf(msg, "UNKNOWN! (%d)", sem); + return msg; + } +} + +static char * +_typestr(int type) +{ + static char msg[20]; + if (type == PM_TYPE_32) + return "PM_TYPE_32"; + else if (type == PM_TYPE_U32) + return "PM_TYPE_U32"; + else if (type == PM_TYPE_64) + return "PM_TYPE_64"; + else if (type == PM_TYPE_U64) + return "PM_TYPE_U64"; + else if (type == PM_TYPE_FLOAT) + return "PM_TYPE_FLOAT"; + else if (type == PM_TYPE_DOUBLE) + return "PM_TYPE_DOUBLE"; + else { + sprintf(msg, "UNKNOWN! (%d)", type); + return msg; + } +} + +#if 0 // debugging +static char * +_ctypestr(int ctype) +{ + if (ctype == PERF_COUNTER_COUNTER) + return "PERF_COUNTER_COUNTER"; + else if (ctype == PERF_RAW_FRACTION) + return "PERF_RAW_FRACTION"; + else if (ctype == PERF_LARGE_RAW_FRACTION) + return "PERF_LARGE_RAW_FRACTION"; + else if (ctype == PERF_COUNTER_LARGE_RAWCOUNT_HEX) + return "PERF_COUNTER_LARGE_RAWCOUNT_HEX"; + else if (ctype == PERF_COUNTER_LARGE_RAWCOUNT) + return "PERF_COUNTER_LARGE_RAWCOUNT"; + else if (ctype == PERF_PRECISION_100NS_TIMER) + return "PERF_PRECISION_100NS_TIMER"; + else if (ctype == PERF_100NSEC_TIMER) + return "PERF_100NSEC_TIMER"; + else if (ctype == PERF_COUNTER_BULK_COUNT) + return "PERF_COUNTER_BULK_COUNT"; + else if (ctype == PERF_COUNTER_RAWCOUNT_HEX) + return "PERF_COUNTER_RAWCOUNT_HEX"; + else if (ctype == PERF_COUNTER_RAWCOUNT) + return "PERF_COUNTER_RAWCOUNT"; + else if (ctype == PERF_COUNTER_COUNTER) + return "PERF_COUNTER_COUNTER"; + else + return "UNKNOWN"; +} +#endif + +/* + * Based on documentation from ... + * http://msdn.microsoft.com/library/default.asp? + * url=/library/en-us/sysinfo/base/osversioninfoex_str.asp + */ +static void +windows_format_uname(OSVERSIONINFOEX osv) +{ + char tbuf[80]; + char *name = NULL; + + switch (osv.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + if (osv.dwMajorVersion == 6 && osv.dwMinorVersion == 1) { + if (osv.wProductType == VER_NT_WORKSTATION) + name = string_append(name, "Windows 7"); + else + name = string_append(name, "Windows Server 2008 R2"); + } + else if (osv.dwMajorVersion == 6 && osv.dwMinorVersion == 0) { + if (osv.wProductType == VER_NT_WORKSTATION) + name = string_append(name, "Windows Vista"); + else + name = string_append(name, "Windows Server 2008"); + } + else if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 2) + name = string_append(name, "Windows Server 2003"); + else if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 1) + name = string_append(name, "Windows XP"); + else if (osv.dwMajorVersion == 5 && osv.dwMinorVersion == 0) + name = string_append(name, "Windows 2000"); + else if (osv.dwMajorVersion <= 4) + name = string_append(name, "Windows NT"); + else { + sprintf(tbuf, "Windows Unknown (%ld.%ld)", + osv.dwMajorVersion, osv.dwMinorVersion); + name = string_append(name, tbuf); + } + + /* service pack and build number etc */ + if (osv.szCSDVersion[0] != '\0') { + name = string_append(name, " "); + name = string_append(name, osv.szCSDVersion); + } + sprintf(tbuf, " Build %ld", osv.dwBuildNumber & 0xFFFF); + windows_build = name + strlen(name) + 1; + windows_uname = string_append(name, tbuf); + break; + + default: + windows_uname = "Windows - Platform Unknown"; + windows_build = "Unknown Build"; + break; + } +} + +void +windows_setup_globals(void) +{ + SYSTEM_INFO sysinfo; + OSVERSIONINFOEX osversion; + + ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO)); + GetSystemInfo(&sysinfo); + windows_pagesize = sysinfo.dwPageSize; + + switch (sysinfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + windows_machine = "x86_64"; + break; + case PROCESSOR_ARCHITECTURE_IA64: + windows_machine = "ia64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + windows_machine = "i686"; + break; + default: + windows_machine = "Unknown"; + break; + } + + ZeroMemory(&osversion, sizeof(OSVERSIONINFOEX)); + osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + GetVersionEx((OSVERSIONINFO *)&osversion); + windows_format_uname(osversion); +} + +static void +windows_verify_metric(pdh_metric_t *mp, PDH_COUNTER_INFO_A *infop) +{ + char *ctr_type; + + mp->ctype = infop->dwType; + + switch (mp->ctype) { + /* + * Pdh metric sematics ... from WinPerf.h + * + * SIZE + * DWORD 32-bit + * LARGE 64-bit + * ZERO no support here + * VARIABLE_LEN no support here + * + * TYPE + * NUMBER PM_SEM_INSTANT + * HEX display in hex (no support here) + * DECIMAL display as decimal + * DEC_1000 display as value / 1000 + * (no support here) + * COUNTER PM_SEM_COUNTER + * VALUE display value (no support here) + * RATE time rate converted + * FRACTION divide value by BASE + * BASE used for FRACTION + * QUEUELEN magic internal queuelen() routines + * (you're joking, right?) + * HISTOGRAM counter begins or ends a histo (?) + * (definitely no support here) + * PRECISION divide counter by private clock (?) + * (definitely no support here) + * TEXT no support here + * ZERO no support here + */ + + /* + * Known 32-bit counters + */ + case PERF_COUNTER_COUNTER: + /* 32-bit PM_SEM_COUNTER */ + if (mp->desc.type != PM_TYPE_32 && mp->desc.type != PM_TYPE_U32) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, + "windows_open: PERF_COUNTER_COUNTER: " + "metric %s: rewrite type from %s to PM_TYPE_U32\n", + pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U32; + } + if (mp->desc.sem != PM_SEM_COUNTER) { + __pmNotifyErr(LOG_ERR, "windows_open: PERF_COUNTER_COUNTER: " + "metric %s: semantics %s (expected %s)\n", + pmIDStr(mp->desc.pmid), _semstr(mp->desc.sem), + _semstr(PM_SEM_COUNTER)); + } + break; + + case PERF_COUNTER_RAWCOUNT: + case PERF_COUNTER_RAWCOUNT_HEX: + if (mp->ctype == PERF_COUNTER_RAWCOUNT) + ctr_type = "PERF_COUNTER_RAWCOUNT"; + else + ctr_type = "PERF_COUNTER_RAWCOUNT_HEX"; + /* 32-bit PM_SEM_INSTANT or PM_SEM_DISCRETE */ + if (mp->desc.type != PM_TYPE_32 && mp->desc.type != PM_TYPE_U32) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, + "windows_open: Warning: %s: metric %s: " + "rewrite type from %s to PM_TYPE_U32\n", + ctr_type, pmIDStr(mp->desc.pmid), + _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U32; + } + break; + + /* + * Known 64-bit counters + */ + case PERF_COUNTER_BULK_COUNT: + /* 64-bit PM_SEM_COUNTER */ + if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, + "windows_open: PERF_COUNTER_BULK_COUNT:" + " metric %s: rewrite type from %s to PM_TYPE_U64\n", + pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U64; + } + if (mp->desc.sem != PM_SEM_COUNTER) { + __pmNotifyErr(LOG_ERR, "windows_open: PERF_COUNTER_BULK_COUNT:" + " metric %s: semantics %s (expected %s)\n", + pmIDStr(mp->desc.pmid), _semstr(mp->desc.sem), + _semstr(PM_SEM_COUNTER)); + mp->desc.sem = PM_SEM_COUNTER; + } + break; + + case PERF_100NSEC_TIMER: + case PERF_PRECISION_100NS_TIMER: + if (mp->ctype == PERF_100NSEC_TIMER) + ctr_type = "PERF_100NSEC_TIMER"; + else + ctr_type = "PERF_PRECISION_100NS_TIMER"; + /* + * 64-bit PM_SEM_COUNTER, units are 100's of nanosecs, + * we shall export 'em as microseconds + */ + if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: " + "metric %s: rewrite type from %s to PM_TYPE_U64\n", + ctr_type, pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U64; + } + if (mp->desc.sem != PM_SEM_COUNTER) { + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: " + "metric %s: semantics %s (expected %s)\n", + ctr_type, pmIDStr(mp->desc.pmid), _semstr(mp->desc.sem), + _semstr(PM_SEM_COUNTER)); + mp->desc.sem = PM_SEM_COUNTER; + } + if (mp->desc.units.dimSpace != 0 || + mp->desc.units.dimTime != 1 || + mp->desc.units.dimCount != 0 || + mp->desc.units.scaleTime != PM_TIME_USEC) { + pmUnits units = mp->desc.units; + mp->desc.units.dimSpace = mp->desc.units.dimCount = 0; + mp->desc.units.scaleSpace = mp->desc.units.scaleCount = 0; + mp->desc.units.dimTime = 1; + mp->desc.units.scaleTime = PM_TIME_USEC; + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: " + "metric %s: rewrite dimension and scale from %s to %s", + ctr_type, pmIDStr(mp->desc.pmid), pmUnitsStr(&units), + pmUnitsStr(&mp->desc.units)); + } + break; + + case PERF_COUNTER_LARGE_RAWCOUNT: + case PERF_COUNTER_LARGE_RAWCOUNT_HEX: + /* 64-bit PM_SEM_INSTANT or PM_SEM_DISCRETE */ + if (mp->desc.type != PM_TYPE_64 && + mp->desc.type != PM_TYPE_U64) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, "windows_open: Warning: " + "PERF_COUNTER_LARGE_RAWCOUNT: metric %s: " + "rewrite type from %s to PM_TYPE_U64\n", + pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U64; + } + break; + + case PERF_RAW_FRACTION: + /* Float PM_SEM_INSTANT or PM_SEM_DISCRETE */ + if (mp->desc.type != PM_TYPE_FLOAT) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, "windows_open: Warning: " + "PERF_RAW_FRACTION: metric %s: " + "rewrite type from %s to PM_TYPE_FLOAT\n", + pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_FLOAT; + } + break; + + case PERF_LARGE_RAW_FRACTION: + /* Double PM_SEM_INSTANT or PM_SEM_DISCRETE */ + if (mp->desc.type != PM_TYPE_DOUBLE) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, "windows_open: Warning: " + "PERF_LARGE_RAW_FRACTION: metric %s: " + "rewrite type from %s to PM_TYPE_DOUBLE\n", + pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_DOUBLE; + } + break; + + case PERF_AVERAGE_BULK: + case PERF_AVERAGE_TIMER: + if (mp->ctype == PERF_AVERAGE_BULK) + ctr_type = "PERF_AVERAGE_BULK"; + else + ctr_type = "PERF_AVERAGE_TIMER"; + /* 64-bit PM_SEM_INSTANT or PM_SEM_DISCRETE */ + if (mp->desc.sem != PM_SEM_INSTANT && mp->desc.sem != PM_SEM_DISCRETE) { + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: " + "metric %s: semantics %s (expected %s)\n", + ctr_type, pmIDStr(mp->desc.pmid), + _semstr(mp->desc.sem), _semstr(PM_SEM_INSTANT)); + mp->desc.sem = PM_SEM_INSTANT; + } + if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s " + "metric %s: rewrite type from %s to PM_TYPE_U64\n", + ctr_type, pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U64; + } + break; + + case PERF_SAMPLE_COUNTER: + ctr_type = "PERF_SAMPLE_COUNTER"; + /* floating point PM_SEM_INSTANT or PM_SEM_DISCRETE */ + if (mp->desc.sem != PM_SEM_INSTANT && mp->desc.sem != PM_SEM_DISCRETE) { + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: " + "metric %s: semantics %s (expected %s)\n", + ctr_type, pmIDStr(mp->desc.pmid), + _semstr(mp->desc.sem), _semstr(PM_SEM_INSTANT)); + mp->desc.sem = PM_SEM_INSTANT; + } + if (mp->desc.type != PM_TYPE_FLOAT && mp->desc.type != PM_TYPE_DOUBLE) { + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s " + "metric %s: rewrite type from %s to PM_TYPE_FLOAT\n", + ctr_type, pmIDStr(mp->desc.pmid), _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_FLOAT; + } + break; + + case PERF_ELAPSED_TIME: + ctr_type = "PERF_ELAPSED_TIME"; + if (mp->desc.units.dimSpace != 0 || + mp->desc.units.dimTime != 1 || + mp->desc.units.dimCount != 0) { + pmUnits units = mp->desc.units; + mp->desc.units.dimSpace = mp->desc.units.dimCount = 0; + mp->desc.units.scaleSpace = mp->desc.units.scaleCount = 0; + mp->desc.units.dimTime = 1; + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s: " + "metric %s: rewrite dimension and scale from %s to %s", + ctr_type, pmIDStr(mp->desc.pmid), pmUnitsStr(&units), + pmUnitsStr(&mp->desc.units)); + } + if (mp->desc.type != PM_TYPE_64 && mp->desc.type != PM_TYPE_U64) { + if (!(mp->flags & M_AUTO64)) + __pmNotifyErr(LOG_ERR, "windows_open: Warning: %s " + "metric %s: rewrite type from %s to PM_TYPE_U64\n", + ctr_type, pmIDStr(mp->desc.pmid), + _typestr(mp->desc.type)); + mp->desc.type = PM_TYPE_U64; + } + break; + + default: + __pmNotifyErr(LOG_ERR, "windows_open: Warning: metric %s: " + "unexpected counter type: %s\n", + pmIDStr(mp->desc.pmid), decode_ctype(infop->dwType)); + } + mp->flags |= M_EXPANDED; +} + +int +windows_inform_metric(pdh_metric_t *pmp, LPTSTR p, pdh_value_t *pvp, + BOOLEAN getExplainText, pdh_metric_inform_t informer) +{ + int sts = -1; + PDH_STATUS pdhsts; + PDH_HQUERY queryhdl = NULL; + PDH_HCOUNTER counterhdl = NULL; + DWORD result_sz; + static DWORD info_sz = 0; + static LPSTR info = NULL; + + pdhsts = PdhOpenQueryA(NULL, 0, &queryhdl); + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "windows_open: PdhOpenQueryA failed: %s\n", + pdherrstr(pdhsts)); + return sts; + } + + pdhsts = PdhAddCounterA(queryhdl, p, pvp->inst, &counterhdl); + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "windows_open: Warning: PdhAddCounterA " + "@ pmid=%s pat=\"%s\": %s\n", + pmIDStr(pmp->desc.pmid), p, pdherrstr(pdhsts)); + PdhCloseQuery(queryhdl); + return sts; + } + + /* + * check PCP metric semantics against PDH info + */ + if (info_sz == 0) { + /* + * We've observed an initial call to PdhGetCounterInfoA() + * hang with a zero sized buffer ... pander to this with + * an initial buffer allocation ... (size is a 100% guess). + */ + info_sz = 256; + if ((info = (LPSTR)malloc(info_sz)) == NULL) { + __pmNotifyErr(LOG_ERR, "windows_open: PdhGetCounterInfoA " + "malloc (%d) failed @ metric %s: ", + (int)info_sz, pmIDStr(pmp->desc.pmid)); + goto done; + } + } + result_sz = info_sz; + pdhsts = PdhGetCounterInfoA(counterhdl, getExplainText, &result_sz, + (PDH_COUNTER_INFO_A *)info); + if (pdhsts == PDH_MORE_DATA) { + info_sz = result_sz; + if ((info = (LPSTR)realloc(info, info_sz)) == NULL) { + __pmNotifyErr(LOG_ERR, "windows_open: PdhGetCounterInfoA " + "realloc (%d) failed @ metric %s: ", + (int)info_sz, pmIDStr(pmp->desc.pmid)); + goto done; + } + pdhsts = PdhGetCounterInfoA(counterhdl, getExplainText, &result_sz, + (PDH_COUNTER_INFO_A *)info); + } + if (pdhsts != ERROR_SUCCESS) { + __pmNotifyErr(LOG_ERR, "windows_open: PdhGetCounterInfoA " + "failed @ metric %s: %s\n", + pmIDStr(pmp->desc.pmid), pdherrstr(pdhsts)); + goto done; + } + else { + informer(pmp, (PDH_COUNTER_INFO_A *)info); + sts = 0; + } + +done: + PdhRemoveCounter(counterhdl); + PdhCloseQuery(queryhdl); + return sts; +} + +void +windows_verify_callback(pdh_metric_t *pmp, LPSTR pat, pdh_value_t *pvp) +{ + int v; + + if (!(pmp->flags & M_VERIFIED)) { + v = windows_inform_metric(pmp, pat, pvp, FALSE, windows_verify_metric); + if (v == 0) + pmp->flags |= M_VERIFIED; + } +} + + +/* + * General purpose metric regex iterator, call out on each instance + */ +int +windows_visit_metric(pdh_metric_t *pmp, pdh_metric_visitor_t visitor) +{ + size_t size; + int index = 0; + PDH_STATUS pdhsts; + DWORD result_sz; + static DWORD pattern_sz = 0; + static LPSTR pattern = NULL; + LPSTR p; + + if (pmp->desc.indom != PM_INDOM_NULL) { + index = pmInDom_serial(pmp->desc.indom); + pmdaCacheOp(pmp->desc.indom, PMDA_CACHE_INACTIVE); + } + + pmp->flags &= ~(M_EXPANDED|M_NOVALUES); + memset(pmp->vals, 0, pmp->num_alloc * sizeof(pdh_value_t)); + pmp->num_vals = 0; + + result_sz = 0; + pdhsts = PdhExpandCounterPathA(pmp->pat, NULL, &result_sz); + if (pdhsts == PDH_MORE_DATA) { + if (result_sz >= pattern_sz) { + pattern_sz = roundup(result_sz, 64); + if ((pattern = (LPSTR)realloc(pattern, pattern_sz)) == NULL) { + __pmNotifyErr(LOG_ERR, "windows_open: PdhExpandCounterPathA " + "realloc (%ld) failed @ metric %s: ", + pattern_sz, pmIDStr(pmp->desc.pmid)); + return -1; + } + } + result_sz = pattern_sz; + pdhsts = PdhExpandCounterPathA(pmp->pat, pattern, &result_sz); + } + if (pdhsts != PDH_CSTATUS_VALID_DATA) { + if (pmp->pat[0] != '\\') { + /* + * Skip metrics that are derived and do not have an explicit + * PDH API retrieval needed ... do nothing here. + */ + ; + } + else if (pmp->flags & M_OPTIONAL) { + pmp->flags |= M_NOVALUES; + return 0; + } + else { + __pmNotifyErr(LOG_ERR, "windows_open: PdhExpandCounterPathA " + "failed @ metric pmid=%s pattern=\"%s\": %s\n", + pmIDStr(pmp->desc.pmid), pmp->pat, pdherrstr(pdhsts)); + } + pmp->flags |= M_NOVALUES; + return -1; + } + + /* + * PdhExpandCounterPathA is apparently busted ... the length + * returned includes one byte _after_ the last NULL byte + * string terminator, but the final byte is apparently + * not being set ... force the issue + */ + pattern[result_sz-1] = '\0'; + for (p = pattern; *p; p += lstrlen(p) + 1) { + pdh_value_t *pvp; + + pmp->num_vals++; + if (pmp->num_vals > pmp->num_alloc) { + size = pmp->num_vals * sizeof(pdh_value_t); + if ((pmp->vals = (pdh_value_t *)realloc(pmp->vals, size)) == NULL) { + __pmNotifyErr(LOG_ERR, "windows_open: Error: values realloc " + "(%d x %d) failed @ metric %s [%s]: ", + pmp->num_vals, sizeof(pdh_value_t), + pmIDStr(pmp->desc.pmid), p); + pmp->num_alloc = 0; + return -1; + } + pmp->num_alloc = pmp->num_vals; + } + + pvp = &pmp->vals[pmp->num_vals-1]; + if (pmp->desc.indom == PM_INDOM_NULL) { + /* singular instance */ + pvp->inst = PM_IN_NULL; + if (pmp->num_vals > 1) { + char *q; + int k; + + /* + * report only once per pattern + */ + __pmNotifyErr(LOG_ERR, "windows_open: Warning: singular " + "metric %s has more than one instance ...\n", + pmIDStr(pmp->desc.pmid)); + fprintf(stderr, " pattern: \"%s\"\n", pmp->pat); + for (k = 0, q = pattern; *q; q += lstrlen(q) + 1, k++) + fprintf(stderr, " match[%d]: \"%s\"\n", k, q); + fprintf(stderr, "... skip this counter\n"); + + /* next realloc() will be a NOP */ + pmp->num_vals--; + + /* no more we can do here, onto next metric-pattern */ + break; + } + } + else { + /* + * if metric has instance domain, parse pattern using + * indom type to extract instance name and number, and + * add into indom cache data structures as needed. + */ + if ((pvp->inst = windows_lookup_instance(p, pmp)) < 0) { + /* + * error reported in windows_check_instance() ... + * we cannot return any values for this instance if + * we don't recognize the name ... skip this one, + * the next realloc() (if any) will be a NOP + */ + pmp->num_vals--; + + /* move onto next instance */ + continue; + } + windows_indom_setup[index] = 1; + } + + if (visitor) + visitor(pmp, p, pvp); + } + + return 0; +} + +void +windows_open(int domain) +{ + int i; + + windows_setup_globals(); + + for (i = 0; i < NUMINDOMS; i++) { + if (windows_indom_fixed(i)) + pmdaCacheOp(INDOM(domain, i), PMDA_CACHE_LOAD); + windows_indom_reset[i] = 0; + } + + /* + * This initialisation can take a long time - we have many metrics + * now for Windows. Better to delay this until we need to do it, + * and then only for the metrics needed. However, we cannot delay + * for those metrics that may change descriptors depending on the + * type of platform (64/32 bit, kernel version, etc), so those we + * verify up-front. + */ + for (i = 0; i < metricdesc_sz; i++) { + if ((metricdesc[i].flags & M_AUTO64) || (pmDebug & DBG_TRACE_LIBPMDA)) + windows_visit_metric(&metricdesc[i], windows_verify_callback); + } + + for (i = 0; i < NUMINDOMS; i++) { + /* Do we want to persist this instance domain to disk? */ + if (windows_indom_reset[i] && windows_indom_fixed(i)) + pmdaCacheOp(INDOM(domain, i), PMDA_CACHE_SAVE); + } +} diff --git a/src/pmdas/windows/pdhlist.c b/src/pmdas/windows/pdhlist.c new file mode 100644 index 0000000..387f139 --- /dev/null +++ b/src/pmdas/windows/pdhlist.c @@ -0,0 +1,85 @@ +/* + * List Windows performance counters on the current platform. + * + * Copyright (c) 2006, Ken McDonell. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include + +static int verbose; +extern char *pdherrstr(int); +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + +void +expand(char *pat) +{ + LPTSTR ptr; + LPSTR buf = NULL; + DWORD bufsz = 0; + int i; + PDH_STATUS pdhsts; + + // Iterate because size grows in first couple of attempts! + for (i = 0; i < 5; i++) { + if (bufsz && (buf = (LPSTR)malloc(bufsz)) == NULL) { + fprintf(stderr, "malloc %ld failed for pattern: %s\n", bufsz, pat); + return; + } + if (verbose) + fprintf(stderr, "ExpandCounters pattern: %s\n", pat); + if ((pdhsts = PdhExpandCounterPathA(pat, buf, &bufsz)) == PDH_MORE_DATA) { + // bufsz has the required length (minus the last NULL) + bufsz = roundup(bufsz + 1, 64); + free(buf); + } + else + break; + } + + if (pdhsts == PDH_CSTATUS_VALID_DATA) { + // success, print all counters + ptr = buf; + while (*ptr) { + printf("%s\n", ptr); + ptr += strlen(ptr) + 1; + } + } + else { + fprintf(stderr, "PdhExpandCounterPathA failed: %s\n", pdherrstr(pdhsts)); + if (pdhsts == PDH_MORE_DATA) + fprintf(stderr, "still need to resize buffer to %ld\n", bufsz); + } + + fflush(stderr); + fflush(stdin); +} + +int +main(int argc, char **argv) +{ + int i; + + if (argc == 1) { + expand("\\*\\*"); + expand("\\*(*)\\*"); + return 0; + } + + for (i = 1; i < argc; i++) + expand(argv[i]); + return 0; +} diff --git a/src/pmdas/windows/pdhmatch.sh b/src/pmdas/windows/pdhmatch.sh new file mode 100644 index 0000000..33c2e98 --- /dev/null +++ b/src/pmdas/windows/pdhmatch.sh @@ -0,0 +1,192 @@ +#!/bin/sh +# +# take the output from pdhlist.exe +# - remove hostname +# - collapse known instance domains to a symbolic representation +# - match up against patterns in data.c +# + +#debug# tmp=/var/tmp/$$ +#debug# trap "rm -f $tmp.*; exit 0" 0 1 2 3 15 +tmp=`pwd`/tmp + +# Examples of instance domains to collapse from pdhlist.exe +# output +# +# SQLServer:Buffer Partition(0)\Free pages +# Job Object Details(Winlogon Job 0-57c89/logon.scr)\% User Time +# Job Object Details(Winlogon Job 0-57c89/_Total)\% User Time +# Job Object Details(WmiProviderSubSystemHostJob/wmiprvse)\% User Time +# Job Object Details(WmiProviderSubSystemHostJob/_Total)\% User Time +# [not] Job Object Details(_Total/_Total)\% User Time +# Job Object(WmiProviderSubSystemHostJob)\Current % User Mode Time +# [not] Job Object(_Total)\Current % User Mode Time +# Thread(Idle/0)\Context Switches/sec +# Thread(csrss/0#1)\Context Switches/sec +# LogicalDisk(C:)\% Free Space +# [not] LogicalDisk(_Total)\% Free Space +# Network Interface(Intel[R] PRO_1000 MT Dual Port Network # Connection _2)\Bytes Received/sec +# PhysicalDisk(0 C:)\% Disk Read Time +# [not] PhysicalDisk(_Total)\% Disk Read Time +# Print Queue(Canon LBP-3260 PCL6 on SCRIBE (from LUKE) in session 1)\Add Network Printer Calls +# [not] Print Queue(_Total)\Add Network Printer Calls +# Process(Idle)\% Privileged Time +# [not] Process(_Total)\% Privileged Time +# Processor(0)\% Idle Time +# [not] Processor(_Total)\% Idle Time +# RAS Port(LPT1)\Alignment Errors +# SQLServer:Databases(alice)\Active Transactions +# [not] SQLServer:Databases(_Total)\Active Transactions +# SQLServer:Locks(Database)\Average Wait Time (ms) +# [not] SQLServer:Locks(_Total)\Lock Requests/sec +# Server Work Queues(3)\Active Threads +# SQLServer:Cache Manager(Adhoc Sql Plans)\Cache Hit Ratio +# Terminal Services Session(Console)\% Privileged Time +# + +if [ $# -eq 1 ] +then + cat $1 +elif [ $# -eq 0 ] +then + cat +else + echo "Usage: $0 [output-file-from-pdhlist]" >&2 + exit 1 +fi \ +| dos2unix \ +| sed >$tmp.tmp \ + -e 's/^\\\\[^\]*\\//' \ + -e '/^SQLServer:Buffer Partition(/s/([0-9]*)\\/()\\/' \ + -e '/^Job Object Details(/{ +/(_Total\//!s/(.*\/_Total)\\/(\/_Total)\\/ +/\/_Total)/!s/(.*\/.*)\\/(\/)\\/ +}' \ + -e '/^Job Object(/{ +/(_Total)/!s/(.*)\\/()\\/ +}' \ + -e '/^Thread(/{ +s/([^/]*\/[0-9]*)\\/(\/)\\/ +s/([^/]*\/[0-9]*#[0-9]*)\\/(\/#)\\/ +}' \ + -e '/^LogicalDisk(/s/([A-Z]:)\\/()\\/' \ + -e '/^Network Interface(/s/([^)]*)\\/()\\/' \ + -e '/^PhysicalDisk(/s/([0-9][0-9]* [A-Z]:)\\/()\\/' \ + -e '/^Print Queue(/{ +/(_Total)/!s/(.*)\\/()\\/ +}' \ + -e '/^Process(/{ +/(_Total)/!s/(.*)\\/()\\/ +}' \ + -e '/^Processor(/{ +/(_Total)/!s/(.*)\\/()\\/ +}' \ + -e '/^RAS Port(/s/(.*)\\/()\\/' \ + -e '/^SQLServer:Databases(/{ +/(_Total)/!s/(.*)\\/()\\/ +}' \ + -e '/^SQLServer:Locks(/{ +/(_Total)/!s/(.*)\\/()\\/ +}' \ + -e '/^Server Work Queues(/s/(.*)\\/()\\/' \ + -e '/^SQLServer:Cache Manager(/s/(.*)\\/()\\/' \ + -e '/^Terminal Services Session(/s/(.*)\\/()\\/' + +# This step tries to deal with this class of cases ... +# pdhlist reports stuff like +# SQLServer:Locks\Average Wait Time (ms) +# SQLServer:Locks(_Total)\Average Wait Time (ms) +# SQLServer:Locks(*/*#*)\Average Wait Time (ms) +# but the first one is in fact bogus (only the second 2 forms +# can be looked up. +# +sed <$tmp.tmp \ + -e '/^.NET CLR Exceptions\\/d' \ + -e '/^.NET CLR Interop\\/d' \ + -e '/^.NET CLR Jit\\/d' \ + -e '/^.NET CLR Loading\\/d' \ + -e '/^.NET CLR LocksAndThreads\\/d' \ + -e '/^.NET CLR Memory\\/d' \ + -e '/^.NET CLR Remoting\\/d' \ + -e '/^.NET CLR Security\\/d' \ + -e '/^NBT Connection\\/d' \ + -e '/^Paging File\\/d' \ + -e '/^SQLServer:User Settable\\/d' \ + -e '/^Server Work Queues\\/d' \ + -e '/^SQLServer:Buffer Partition\\/d' \ + -e '/^Job Object Details\\/d' \ + -e '/^Job Object\\/d' \ + -e '/^Thread\\/d' \ + -e '/^LogicalDisk\\/d' \ + -e '/^Network Interface\\/d' \ + -e '/^PhysicalDisk\\/d' \ + -e '/^Print Queue\\/d' \ + -e '/^Process\\/d' \ + -e '/^Processor\\/d' \ + -e '/^RAS Port\\/d' \ + -e '/^SQLServer:Databases\\/d' \ + -e '/^SQLServer:Locks\\/d' \ +| LC_COLLATE=POSIX sort \ +| uniq >$tmp.munged + +# extract patterns from PMDA source +# +if [ -f data.c ] +then + sed -n )\\/' \ + -e '/^PhysicalDisk(/s/(\*\/\*#\*)\\/()\\/' \ + -e '/^Processor(/s/(\*\/\*#\*)\\/()\\/' \ + -e '/^SQLServer:Locks(/s/(\*\/\*#\*)\\/()\\/' \ + -e '/^LogicalDisk(/s/(\*\/\*#\*)\\/()\\/' \ + | LC_COLLATE=POSIX sort \ + | uniq >$tmp.pmda + +else + echo "Warning: no data.c, cannot match metrics up with PMDA patterns" >&2 + sed -e 's/^/? /' <$tmp.munged +fi + +# match 'em up +# + +comm -23 $tmp.pmda $tmp.munged >$tmp.tmp +if [ -s $tmp.tmp ] +then + echo "============================================" + echo "=== Warning: These current PMDA patterns do NOT match ANY metrics ..." + echo "============================================" + cat $tmp.tmp + echo +fi + +comm -12 $tmp.pmda $tmp.munged >$tmp.tmp +if [ -s $tmp.tmp ] +then + echo "============================================" + echo "=== Metrics supported in the current PMDA ..." + echo "============================================" + cat $tmp.tmp +else + echo "============================================" + echo "=== Warning: The current PMDA patterns match NO metric!" + echo "============================================" +fi + +comm -13 $tmp.pmda $tmp.munged >$tmp.tmp +if [ -s $tmp.tmp ] +then + echo + echo "============================================" + echo "=== Metrics NOT supported in the current PMDA" + echo "============================================" + cat $tmp.tmp +fi diff --git a/src/pmdas/windows/pmda.c b/src/pmdas/windows/pmda.c new file mode 100644 index 0000000..0c5009a --- /dev/null +++ b/src/pmdas/windows/pmda.c @@ -0,0 +1,1601 @@ +/* + * Windows PMDA + * + * Copyright (c) 2008-2010 Aconex. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include "hypnotoad.h" +#include + +/* + * Array of all metrics - the PMID item field indexes this directly. + */ +pdh_metric_t metricdesc[] = { +/* kernel.all.cpu.user */ + { { PMDA_PMID(0,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(_Total)\\% User Time" + }, +/* kernel.all.cpu.idle */ + { { PMDA_PMID(0,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(_Total)\\% Idle Time" + }, +/* kernel.all.cpu.sys */ + { { PMDA_PMID(0,2), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(_Total)\\% Privileged Time" + }, +/* kernel.all.cpu.intr */ + { { PMDA_PMID(0,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(_Total)\\% Interrupt Time" + }, +/* kernel.percpu.cpu.user */ + { { PMDA_PMID(0,4), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(*)\\% User Time" + }, +/* kernel.percpu.cpu.idle */ + { { PMDA_PMID(0,5), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(*)\\% Idle Time" + }, +/* kernel.percpu.cpu.sys */ + { { PMDA_PMID(0,6), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(*)\\% Privileged Time" + }, +/* kernel.percpu.cpu.intr */ + { { PMDA_PMID(0,7), PM_TYPE_U64, CPU_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Processor(*)\\% Interrupt Time" + }, +/* kernel.num_processes */ + { { PMDA_PMID(0,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\System\\Processes" + }, +/* kernel.num_threads */ + { { PMDA_PMID(0,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\System\\Threads" + }, +/* kernel.all.pswitch */ + { { PMDA_PMID(0,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\System\\Context Switches/sec" + }, +/* kernel.all.file.read */ + { { PMDA_PMID(0,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\System\\File Read Operations/sec" + }, +/* kernel.all.file.write */ + { { PMDA_PMID(0,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\System\\File Write Operations/sec" + }, +/* kernel.all.file.read_bytes */ + { { PMDA_PMID(0,13), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\System\\File Read Bytes/sec" + }, +/* kernel.all.file.write_bytes */ + { { PMDA_PMID(0,14), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\System\\File Write Bytes/sec" + }, +/* disk.all.read */ + { { PMDA_PMID(0,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Disk Reads/sec" + }, +/* disk.all.write */ + { { PMDA_PMID(0,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Disk Writes/sec" + }, +/* disk.all.total */ + { { PMDA_PMID(0,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Disk Transfers/sec" + }, +/* disk.all.read_bytes */ + { { PMDA_PMID(0,18), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Disk Read Bytes/sec" + }, +/* disk.all.write_bytes */ + { { PMDA_PMID(0,19), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Disk Write Bytes/sec" + }, +/* disk.all.total_bytes */ + { { PMDA_PMID(0,20), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Disk Bytes/sec" + }, +/* disk.dev.read */ + { { PMDA_PMID(0,21), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Disk Reads/sec" + }, +/* disk.dev.write */ + { { PMDA_PMID(0,22), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Disk Writes/sec" + }, +/* disk.dev.total */ + { { PMDA_PMID(0,23), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Disk Transfers/sec" + }, +/* disk.dev.read_bytes */ + { { PMDA_PMID(0,24), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Disk Read Bytes/sec" + }, +/* disk.dev.write_bytes */ + { { PMDA_PMID(0,25), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Disk Write Bytes/sec" + }, +/* disk.dev.total_bytes */ + { { PMDA_PMID(0,26), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Disk Bytes/sec" + }, +/* mem.page_faults */ + { { PMDA_PMID(0,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Page Faults/sec" + }, +/* mem.available */ + { { PMDA_PMID(0,28), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Available MBytes" + }, +/* mem.committed_bytes */ + { { PMDA_PMID(0,29), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Committed Bytes" + }, +/* mem.pool.paged_bytes */ + { { PMDA_PMID(0,30), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pool Paged Bytes" + }, +/* mem.pool.non_paged_bytes */ + { { PMDA_PMID(0,31), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pool Nonpaged Bytes" + }, +/* mem.cache.lazy_writes */ + { { PMDA_PMID(0,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Cache\\Lazy Write Flushes/sec" + }, +/* mem.cache.lazy_write_pages */ + { { PMDA_PMID(0,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Cache\\Lazy Write Pages/sec" + }, +/* mem.cache.mdl.read */ + { { PMDA_PMID(0,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Cache\\MDL Reads/sec" + }, +/* mem.cache.read_ahead */ + { { PMDA_PMID(0,35), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Cache\\Read Aheads/sec" + }, +/* mem.cache.mdl.sync_read */ + { { PMDA_PMID(0,36), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Cache\\Sync MDL Reads/sec" + }, +/* mem.cache.mdl.async_read */ + { { PMDA_PMID(0,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Cache\\Async MDL Reads/sec" + }, +/* network.interface.in.packets */ + { { PMDA_PMID(0,38), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Packets Received/sec" + }, +/* network.interface.in.bytes */ + { { PMDA_PMID(0,39), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0,0, PM_SPACE_BYTE, 0,0) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Bytes Received/sec" + }, +/* network.interface.in.errors */ + { { PMDA_PMID(0,40), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Packets Received Errors" + }, +/* network.interface.out.packets */ + { { PMDA_PMID(0,41), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Packets Sent/sec" + }, +/* network.interface.out.bytes */ + { { PMDA_PMID(0,42), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0,0, PM_SPACE_BYTE, 0,0) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Bytes Sent/sec" + }, +/* network.interface.out.errors */ + { { PMDA_PMID(0,43), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Packets Outbound Errors" + }, +/* network.interface.total.packets */ + { { PMDA_PMID(0,44), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_NONE | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Packets/sec" + }, +/* network.interface.total.bytes */ + { { PMDA_PMID(0,45), PM_TYPE_U64, NETIF_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, + M_NONE, 0, 0, 0, NULL, + "\\Network Interface(*)\\Bytes Total/sec" + }, +/* sqlserver.buf_mgr.cache_hit_ratio */ + { { PMDA_PMID(0,46), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Buffer cache hit ratio" + }, +/* sqlserver.buf_mgr.page_lookups */ + { { PMDA_PMID(0,47), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Page lookups/sec" + }, +/* sqlserver.buf_mgr.free_list_stalls */ + { { PMDA_PMID(0,48), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Free list stalls/sec" + }, +/* sqlserver.buf_mgr.free_pages */ + { { PMDA_PMID(0,49), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Free pages" + }, +/* sqlserver.buf_mgr.total_pages */ + { { PMDA_PMID(0,50), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Total pages" + }, +/* sqlserver.buf_mgr.target_pages */ + { { PMDA_PMID(0,51), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Target pages" + }, +/* sqlserver.buf_mgr.database_pages */ + { { PMDA_PMID(0,52), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Database pages" + }, +/* sqlserver.buf_mgr.reserved_pages */ + { { PMDA_PMID(0,53), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Reserved pages" + }, +/* sqlserver.buf_mgr.stolen_pages */ + { { PMDA_PMID(0,54), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Stolen pages" + }, +/* sqlserver.buf_mgr.lazy_writes */ + { { PMDA_PMID(0,55), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Lazy writes/sec" + }, +/* sqlserver.buf_mgr.readahead_pages */ + { { PMDA_PMID(0,56), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Readahead pages/sec" + }, +/* sqlserver.buf_mgr.procedure_cache_pages */ + { { PMDA_PMID(0,57), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Procedure cache pages" + }, +/* sqlserver.buf_mgr.page_reads */ + { { PMDA_PMID(0,58), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Page reads/sec" + }, +/* sqlserver.buf_mgr.page_writes */ + { { PMDA_PMID(0,59), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Page writes/sec" + }, +/* sqlserver.buf_mgr.checkpoint_pages */ + { { PMDA_PMID(0,60), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Checkpoint pages/sec" + }, +/* sqlserver.buf_mgr.awe.lookup_maps */ + { { PMDA_PMID(0,61), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\AWE lookup maps/sec" + }, +/* sqlserver.buf_mgr.awe.stolen_maps */ + { { PMDA_PMID(0,62), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\AWE stolen maps/sec" + }, +/* sqlserver.buf_mgr.awe.write_maps */ + { { PMDA_PMID(0,63), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\AWE write maps/sec" + }, +/* sqlserver.buf_mgr.awe.unmap_calls */ + { { PMDA_PMID(0,64), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\AWE unmap calls/sec" + }, +/* sqlserver.buf_mgr.awe.unmap_pages */ + { { PMDA_PMID(0,65), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\AWE unmap pages/sec" + }, +/* sqlserver.buf_mgr.page_life_expectancy */ + { { PMDA_PMID(0,66), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Buffer Manager\\Page life expectancy" + }, +/* filesys.full */ + { { PMDA_PMID(0,67), PM_TYPE_FLOAT, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\LogicalDisk(*)\\% Free Space" + }, +/* disk.dev.idle */ + { { PMDA_PMID(0,68), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\% Idle Time" + }, +/* sqlserver.locks.all.requests */ + { { PMDA_PMID(0,69), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(_Total)\\Lock Requests/sec" + }, +/* sqlserver.locks.all.waits */ + { { PMDA_PMID(0,70), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(_Total)\\Lock Waits/sec" + }, +/* sqlserver.locks.all.deadlocks */ + { { PMDA_PMID(0,71), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(_Total)\\Number of Deadlocks/sec" + }, +/* sqlserver.locks.all.timeouts */ + { { PMDA_PMID(0,72), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(_Total)\\Lock Timeouts/sec" + }, +/* sqlserver.locks.all.wait_time */ + { { PMDA_PMID(0,73), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(_Total)\\Lock Wait Time (ms)" + }, +/* sqlserver.locks.all.avg_wait_time */ + { { PMDA_PMID(0,74), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(_Total)\\Average Wait Time (ms)" + }, +/* sqlserver.locks.region.requests */ + { { PMDA_PMID(0,75), PM_TYPE_U32, SQL_LOCK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(*)\\Lock Requests/sec" + }, +/* sqlserver.locks.region.waits */ + { { PMDA_PMID(0,76), PM_TYPE_U32, SQL_LOCK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(*)\\Lock Waits/sec" + }, +/* sqlserver.locks.region.deadlocks */ + { { PMDA_PMID(0,77), PM_TYPE_U32, SQL_LOCK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(*)\\Number of Deadlocks/sec" + }, +/* sqlserver.locks.region.timeouts */ + { { PMDA_PMID(0,78), PM_TYPE_U32, SQL_LOCK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(*)\\Lock Timeouts/sec" + }, +/* sqlserver.locks.region.wait_time */ + { { PMDA_PMID(0,79), PM_TYPE_U32, SQL_LOCK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(*)\\Lock Wait Time (ms)" + }, +/* sqlserver.locks.region.avg_wait */ + { { PMDA_PMID(0,80), PM_TYPE_FLOAT, SQL_LOCK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Locks(*)\\Average Wait Time (ms)" + }, +/* sqlserver.cache_mgr.all.cache_hit_ratio */ + { { PMDA_PMID(0,81), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(_Total)\\Cache Hit Ratio" + }, +/* sqlserver.cache_mgr.cache.cache_hit_ratio */ + { { PMDA_PMID(0,82), PM_TYPE_FLOAT, SQL_CACHE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(*)\\Cache Hit Ratio" + }, +/* sqlserver.connections */ + { { PMDA_PMID(0,83), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:General Statistics\\User Connections" + }, +/* sqlserver.databases.all.transactions */ + { { PMDA_PMID(0,84), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Transactions/sec" + }, +/* sqlserver.databases.db.transactions */ + { { PMDA_PMID(0,85), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Transactions/sec" + }, +/* sqlserver.sql.batch_requests */ + { { PMDA_PMID(0,86), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:SQL Statistics\\Batch Requests/sec" + }, +/* sqlserver.latches.waits */ + { { PMDA_PMID(0,87), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Latches\\Latch Waits/sec" + }, +/* sqlserver.latches.wait_time */ + { { PMDA_PMID(0,88), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Latches\\Total Latch Wait Time (ms)" + }, +/* sqlserver.latches.avg_wait_time */ + { { PMDA_PMID(0,89), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_MSEC, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Latches\\Average Latch Wait Time (ms)" + }, +/* sqlserver.databases.all.data_file_size */ + { { PMDA_PMID(0,90), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Data File(s) Size (KB)" + }, +/* sqlserver.databases.all.log_file_size */ + { { PMDA_PMID(0,91), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Log File(s) Size (KB)" + }, +/* sqlserver.databases.all.log_file_used */ + { { PMDA_PMID(0,92), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Log File(s) Used Size (KB)" + }, +/* sqlserver.databases.db.data_file_size */ + { { PMDA_PMID(0,93), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Data File(s) Size (KB)" + }, +/* sqlserver.databases.db.log_file_size */ + { { PMDA_PMID(0,94), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Log File(s) Size (KB)" + }, +/* sqlserver.databases.db.log_file_used */ + { { PMDA_PMID(0,95), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Log File(s) Used Size (KB)" + }, +/* sqlserver.sql.compilations */ + { { PMDA_PMID(0,96), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:SQL Statistics\\SQL Compilations/sec" + }, +/* sqlserver.sql.re_compilations */ + { { PMDA_PMID(0,97), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:SQL Statistics\\SQL Re-Compilations/sec" + }, +/* sqlserver.access.full_scans */ + { { PMDA_PMID(0,98), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Access Methods\\Full Scans/sec" + }, +/* sqlserver.access.pages_allocated */ + { { PMDA_PMID(0,99), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Access Methods\\Pages Allocated/sec" + }, +/* sqlserver.access.table_lock_escalations */ + { { PMDA_PMID(0,100), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Access Methods\\Table Lock Escalations/sec" + }, +/* disk.dev.queuelen */ + { { PMDA_PMID(0,101), PM_TYPE_U32, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Current Disk Queue Length" + }, +/* sqlserver.databases.all.log_flushes */ + { { PMDA_PMID(0,102), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Log Flushes/sec" + }, +/* sqlserver.databases.db.log_flushes */ + { { PMDA_PMID(0,103), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Log Flushes/sec" + }, +/* sqlserver.databases.all.log_bytes_flushed */ + { { PMDA_PMID(0,104), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Log Bytes Flushed/sec" + }, +/* sqlserver.databases.db.log_bytes_flushed */ + { { PMDA_PMID(0,105), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Log Bytes Flushed/sec" + }, +/* hinv.physmem */ + { { PMDA_PMID(0,106), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_MBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* hinv.ncpu */ + { { PMDA_PMID(0,107), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* hinv.ndisk */ + { { PMDA_PMID(0,108), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* kernel.uname.distro */ + { { PMDA_PMID(0,109), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* kernel.uname.release */ + { { PMDA_PMID(0,110), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* kernel.uname.version */ + { { PMDA_PMID(0,111), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* kernel.uname.sysname */ + { { PMDA_PMID(0,112), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* kernel.uname.machine */ + { { PMDA_PMID(0,113), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* kernel.uname.nodename */ + { { PMDA_PMID(0,114), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* pmda.uname */ + { { PMDA_PMID(0,115), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* pmda.version */ + { { PMDA_PMID(0,116), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, "" + }, + +/* filesys.capacity */ + { { PMDA_PMID(0,117), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, M_REDO, 0, 0, 0, NULL, "" + }, +/* filesys.used */ + { { PMDA_PMID(0,118), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, M_REDO, 0, 0, 0, NULL, "" + }, +/* filesys.free */ + { { PMDA_PMID(0,119), PM_TYPE_U64, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, M_REDO, 0, 0, 0, NULL, "" + }, +/* dummy - filesys.free_space */ + { { PMDA_PMID(0,120), PM_TYPE_U32, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_MBYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\LogicalDisk(*)\\Free Megabytes" + }, +/* dummy - filesys.free_percent */ + { { PMDA_PMID(0,121), PM_TYPE_FLOAT, FILESYS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\LogicalDisk(*)\\% Free Space" + }, +/* sqlserver.access.page_splits */ + { { PMDA_PMID(0,122), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Access Methods\\Page Splits/sec" + }, +/* network.tcp.activeopens */ + { { PMDA_PMID(0,123), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Connections Active" + }, +/* network.tcp.passiveopens */ + { { PMDA_PMID(0,124), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Connections Passive" + }, +/* network.tcp.attemptfails */ + { { PMDA_PMID(0,125), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Connection Failures" + }, +/* network.tcp.estabresets */ + { { PMDA_PMID(0,126), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Connections Reset" + }, +/* network.tcp.currestab */ + { { PMDA_PMID(0,127), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Connections Established" + }, +/* network.tcp.insegs */ + { { PMDA_PMID(0,128), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Segments Received/sec" + }, +/* network.tcp.outsegs */ + { { PMDA_PMID(0,129), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Segments Sent/sec" + }, +/* network.tcp.totalsegs */ + { { PMDA_PMID(0,130), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Segments/sec" + }, +/* network.tcp.retranssegs */ + { { PMDA_PMID(0,131), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\TCPv4\\Segments Retransmitted/sec" + }, + +/* disk.all.split_io */ + { { PMDA_PMID(0,132), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Split IO/Sec" + }, +/* disk.dev.split_io */ + { { PMDA_PMID(0,133), PM_TYPE_U32, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Split IO/Sec" + }, + +/* sqlserver.databases.all.active_transactions */ + { { PMDA_PMID(0,134), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(_Total)\\Active Transactions" + }, +/* sqlserver.databases.db.active_transactions */ + { { PMDA_PMID(0,135), PM_TYPE_U32, SQL_DB_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) + }, M_REDO | M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Databases(*)\\Active Transactions" + }, + +/* mem.commit_limit */ + { { PMDA_PMID(0,136), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Commit Limit" + }, +/* mem.write_copies */ + { { PMDA_PMID(0,137), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Write Copies/sec" + }, +/* mem.transition_faults */ + { { PMDA_PMID(0,138), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Transition Faults/sec" + }, +/* mem.cache.faults */ + { { PMDA_PMID(0,139), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Cache Faults/sec" + }, +/* mem.demand_zero_faults */ + { { PMDA_PMID(0,140), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Demand Zero Faults/sec" + }, +/* mem.pages_total */ + { { PMDA_PMID(0,141), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pages/sec" + }, +/* mem.page_reads */ + { { PMDA_PMID(0,142), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Page Reads/sec" + }, +/* mem.pages_output */ + { { PMDA_PMID(0,143), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pages Output/sec" + }, +/* mem.page_writes */ + { { PMDA_PMID(0,144), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Page Writes/sec" + }, +/* mem.pool.paged_allocs */ + { { PMDA_PMID(0,145), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pool Paged Allocs" + }, +/* mem.pool.nonpaged_allocs */ + { { PMDA_PMID(0,146), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pool Nonpaged Allocs" + }, +/* mem.system.free_ptes */ + { { PMDA_PMID(0,147), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Free System Page Table Entries" + }, +/* mem.cache.bytes */ + { { PMDA_PMID(0,148), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Page Faults/sec" + }, +/* mem.cache.bytes_peak */ + { { PMDA_PMID(0,149), PM_TYPE_64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Cache Bytes Peak" + }, +/* mem.pool.paged_resident_bytes */ + { { PMDA_PMID(0,150), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\Pool Paged Resident Bytes" + }, +/* mem.system.total_code_bytes */ + { { PMDA_PMID(0,151), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\System Code Total Bytes" + }, +/* mem.system.resident_code_bytes */ + { { PMDA_PMID(0,152), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_NONE, 0, 0, 0, NULL, + "\\Memory\\System Code Resident Bytes" + }, + +/* sqlserver.mem_mgr.connection_memory */ + { { PMDA_PMID(0,153), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Connection Memory (KB)" + }, +/* sqlserver.mem_mgr.granted_workspace */ + { { PMDA_PMID(0,154), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Granted Workspace Memory (KB)" + }, +/* sqlserver.mem_mgr.lock_memory */ + { { PMDA_PMID(0,155), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Lock Memory (KB)" + }, +/* sqlserver.mem_mgr.lock_blocks_allocated */ + { { PMDA_PMID(0,156), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Lock Blocks Allocated" + }, +/* sqlserver.mem_mgr.lock_owner_blocks_allocated */ + { { PMDA_PMID(0,157), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Lock Owner Blocks Allocated" + }, +/* sqlserver.mem_mgr.lock_blocks */ + { { PMDA_PMID(0,158), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Lock Blocks" + }, +/* sqlserver.mem_mgr.lock_owner_blocks */ + { { PMDA_PMID(0,159), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Lock Owner Blocks" + }, +/* sqlserver.mem_mgr.maximum_workspace_memory */ + { { PMDA_PMID(0,160), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Maximum Workspace Memory (KB)" + }, +/* sqlserver.mem_mgr.memory_grants_outstanding */ + { { PMDA_PMID(0,161), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Memory Grants Outstanding" + }, +/* sqlserver.mem_mgr.memory_grants_pending */ + { { PMDA_PMID(0,162), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Memory Grants Pending" + }, +/* sqlserver.mem_mgr.optimizer_memory */ + { { PMDA_PMID(0,163), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Optimizer Memory (KB)" + }, +/* sqlserver.mem_mgr.sql_cache_memory */ + { { PMDA_PMID(0,164), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\SQL Cache Memory (KB)" + }, +/* sqlserver.mem_mgr.target_server_memory */ + { { PMDA_PMID(0,165), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Target Server Memory(KB)" + }, +/* sqlserver.mem_mgr.total_server_memory */ + { { PMDA_PMID(0,166), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,PM_SPACE_KBYTE,0,0,0) + }, M_OPTIONAL | M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:Memory Manager\\Total Server Memory (KB)" + }, +/* sqlserver.cache_mgr.all.cache_pages */ + { { PMDA_PMID(0,167), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(_Total)\\Cache Pages" + }, +/* sqlserver.cache_mgr.all.cache_object_count */ + { { PMDA_PMID(0,168), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(_Total)\\Cache Object Counts" + }, +/* sqlserver.cache_mgr.all.cache_use */ + { { PMDA_PMID(0,169), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(_Total)\\Cache Use Counts/sec" + }, +/* sqlserver.cache_mgr.cache.cache_pages */ + { { PMDA_PMID(0,170), PM_TYPE_U32, SQL_CACHE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(*)\\Cache Pages" + }, +/* sqlserver.cache_mgr.cache.cache_object_count */ + { { PMDA_PMID(0,171), PM_TYPE_32, SQL_CACHE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(*)\\Cache Object Counts" + }, +/* sqlserver.cache_mgr.cache.cache_use */ + { { PMDA_PMID(0,172), PM_TYPE_U32, SQL_CACHE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, M_OPTIONAL, 0, 0, 0, NULL, + "\\SQLServer:Cache Manager(*)\\Cache Use Counts/sec" + }, +/* process.count */ + { { PMDA_PMID(0,173), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\Objects\\Processes" + }, +/* process.psinfo.pid */ + { { PMDA_PMID(0,174), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\ID Process" + }, +/* process.psinfo.ppid */ + { { PMDA_PMID(0,175), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Creating Process ID" + }, +/* process.psinfo.cpu_time */ + { { PMDA_PMID(0,176), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Process(*)\\% Processor Time" + }, +/* process.psinfo.elapsed_time */ + { { PMDA_PMID(0,177), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Elapsed Time" + }, +/* process.psinfo.utime */ + { { PMDA_PMID(0,178), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\% User Time" + }, +/* process.psinfo.stime */ + { { PMDA_PMID(0,179), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\% Privileged Time" + }, +/* process.psinfo.nthreads */ + { { PMDA_PMID(0,180), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Thread Count" + }, +/* process.psinfo.priority_base */ + { { PMDA_PMID(0,181), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Priority Base" + }, +/* process.psinfo.nhandles */ + { { PMDA_PMID(0,182), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Handle Count" + }, +/* process.psinfo.page_faults */ + { { PMDA_PMID(0,183), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Page Faults/sec" + }, +/* process.memory.size */ + { { PMDA_PMID(0,184), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Pool Paged Bytes" + }, +/* process.memory.rss */ + { { PMDA_PMID(0,185), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Working Set" + }, +/* process.memory.rss_peak */ + { { PMDA_PMID(0,186), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Working Set Peak" + }, +/* process.memory.virtual */ + { { PMDA_PMID(0,187), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Virtual Bytes" + }, +/* process.memory.virtual_peak */ + { { PMDA_PMID(0,188), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Virtual Bytes Peak" + }, +/* process.memory.page_file */ + { { PMDA_PMID(0,189), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Page File Bytes" + }, +/* process.memory.page_file_peak */ + { { PMDA_PMID(0,190), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Page File Bytes Peak" + }, +/* process.memory.private */ + { { PMDA_PMID(0,191), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Private Bytes" + }, +/* process.memory.pool_paged */ + { { PMDA_PMID(0,192), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Pool Paged Bytes" + }, +/* process.memory.pool_nonpaged */ + { { PMDA_PMID(0,193), PM_TYPE_U32, PROCESS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\Pool Nonpaged Bytes" + }, +/* process.io.reads */ + { { PMDA_PMID(0,194), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Read Operations/sec" + }, +/* process.io.writes */ + { { PMDA_PMID(0,195), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Write Operations/sec" + }, +/* process.io.data */ + { { PMDA_PMID(0,196), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Data Operations/sec" + }, +/* process.io.other */ + { { PMDA_PMID(0,197), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Other Operations/sec" + }, +/* process.io.read_bytes */ + { { PMDA_PMID(0,198), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Read Bytes/sec" + }, +/* process.io.write_bytes */ + { { PMDA_PMID(0,199), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Write Bytes/sec" + }, +/* process.io.data_bytes */ + { { PMDA_PMID(0,200), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Data Bytes/sec" + }, +/* process.io.other_bytes */ + { { PMDA_PMID(0,201), PM_TYPE_U64, PROCESS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Process(*)\\IO Other Bytes/sec" + }, +/* process.thread.context_switches */ + { { PMDA_PMID(0,202), PM_TYPE_U32, THREAD_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Context Switches/sec" + }, +/* process.thread.cpu_time */ + { { PMDA_PMID(0,203), PM_TYPE_U64, THREAD_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\% Processor Time" + }, +/* process.thread.utime */ + { { PMDA_PMID(0,204), PM_TYPE_U64, THREAD_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\% User Time" + }, +/* process.thread.stime */ + { { PMDA_PMID(0,205), PM_TYPE_U64, THREAD_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\% Privileged Time" + }, +/* process.thread.elapsed_time */ + { { PMDA_PMID(0,206), PM_TYPE_U64, THREAD_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Elapsed Time" + }, +/* process.thread.priority */ + { { PMDA_PMID(0,207), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Priority Current" + }, +/* process.thread.priority_base */ + { { PMDA_PMID(0,208), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Priority Base" + }, +/* process.thread.start_address */ + { { PMDA_PMID(0,209), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Start Address" + }, +/* process.thread.state */ + { { PMDA_PMID(0,210), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Thread State" + }, +/* process.thread.wait_reason */ + { { PMDA_PMID(0,211), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\Thread Wait Reason" + }, +/* process.thread.process_id */ + { { PMDA_PMID(0,212), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\ID Process" + }, +/* process.thread.thread_id */ + { { PMDA_PMID(0,213), PM_TYPE_U32, THREAD_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\Thread(*)\\ID Thread" + }, + +/* disk.all.read_time */ + { { PMDA_PMID(0,214), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\% Disk Read Time" + }, +/* disk.all.write_time */ + { { PMDA_PMID(0,215), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\% Disk Write Time" + }, +/* disk.all.total_time */ + { { PMDA_PMID(0,216), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\% Disk Time" + }, +/* disk.dev.read_time */ + { { PMDA_PMID(0,217), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\% Disk Read Time" + }, +/* disk.dev.write_time */ + { { PMDA_PMID(0,218), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\% Disk Write Time" + }, +/* disk.dev.total_time */ + { { PMDA_PMID(0,219), PM_TYPE_U64, DISK_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\% Disk Time" + }, +/* disk.all.average.read_bytes */ + { { PMDA_PMID(0,220), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Avg. Disk Bytes/Read" + }, +/* disk.all.average.write_bytes */ + { { PMDA_PMID(0,221), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Avg. Disk Bytes/Write" + }, +/* disk.all.average.total_bytes */ + { { PMDA_PMID(0,222), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Avg. Disk Bytes/Transfer" + }, +/* disk.all.average.read_time */ + { { PMDA_PMID(0,223), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Avg. Disk sec/Read" + }, +/* disk.all.average.write_time */ + { { PMDA_PMID(0,224), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Avg. Disk sec/Write" + }, +/* disk.all.average.total_time */ + { { PMDA_PMID(0,225), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_NONE, 0, 0, 0, NULL, + "\\PhysicalDisk(_Total)\\Avg. Disk sec/Transfer" + }, +/* disk.dev.average.read_bytes */ + { { PMDA_PMID(0,226), PM_TYPE_U64, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Avg. Disk Bytes/Read" + }, +/* disk.dev.write_bytes */ + { { PMDA_PMID(0,227), PM_TYPE_U64, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Avg. Disk Bytes/Write" + }, +/* disk.dev.total_bytes */ + { { PMDA_PMID(0,228), PM_TYPE_U64, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_BYTE, 0, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Avg. Disk Bytes/Transfer" + }, +/* disk.dev.average.read_time */ + { { PMDA_PMID(0,229), PM_TYPE_U64, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Avg. Disk sec/Read" + }, +/* disk.dev.average.write_time */ + { { PMDA_PMID(0,230), PM_TYPE_U64, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Avg. Disk sec/Write" + }, +/* disk.dev.average.total_time */ + { { PMDA_PMID(0,231), PM_TYPE_U64, DISK_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_USEC, 0) }, M_REDO, 0, 0, 0, NULL, + "\\PhysicalDisk(*)\\Avg. Disk sec/Transfer" + }, + +/* hinv.nfilesys */ + { { PMDA_PMID(0,232), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0, 0, 1, 0, 0, PM_COUNT_ONE) }, M_NONE, 0, 0, 0, NULL, "" + }, +/* hinv.pagesize */ + { { PMDA_PMID(0,233), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_NONE, 0, 0, 0, NULL, "" + }, + +/* kernel.all.uptime */ + { { PMDA_PMID(0,234), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, M_REDO, 0, 0, 0, NULL, + "\\System\\System Up Time" + }, + +/* network.interface.bandwidth */ + { { PMDA_PMID(0,235), PM_TYPE_U32, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, -1, 0, 0, PM_TIME_SEC, 0) + }, M_REDO | M_AUTO64, 0, 0, 0, NULL, + "\\Network Interface(*)\\Current Bandwidth" + }, +/* network.interface.speed */ + { { PMDA_PMID(0,236), PM_TYPE_FLOAT, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, -1, 0, PM_SPACE_MBYTE, PM_TIME_SEC, 0) + }, M_REDO, 0, 0, 0, NULL, "" + }, +/* network.interface.baudrate */ + { { PMDA_PMID(0,237), PM_TYPE_U32, NETIF_INDOM, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, -1, 0, PM_SPACE_BYTE, PM_TIME_SEC, 0) + }, M_REDO, 0, 0, 0, NULL, "" + }, + +/* sqlserver.user_settable.query */ + { { PMDA_PMID(0,238), PM_TYPE_U32, SQL_USER_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, M_AUTO64, 0, 0, 0, NULL, + "\\SQLServer:User Settable(*)\\Query" + }, + +/* mem.physmem */ + { { PMDA_PMID(1,0), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "The amount of actual physical memory" + }, +/* mem.freemem */ + { { PMDA_PMID(1,1), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "The amount of physical memory currently available" + }, +/* mem.util.load */ + { { PMDA_PMID(1,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0, 0, 0, 0, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "Approximate percentage of physical memory in use" + }, +/* mem.util.used */ + { { PMDA_PMID(1,3), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "Amount of physical memory in use" + }, +/* mem.util.free */ + { { PMDA_PMID(1,4), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "Amount of physical memory currently available" + }, +/* swap.length */ + { { PMDA_PMID(1,5), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "The current committed memory limit for the system" + }, +/* swap.used */ + { { PMDA_PMID(1,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "The current committed memory for the system" + }, +/* swap.free */ + { { PMDA_PMID(1,7), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(1, 0, 0, PM_SPACE_KBYTE, 0, 0) }, M_NONE, 0, 0, 0, NULL, + "The maximum amount of memory the system can commit" + }, +}; +int metricdesc_sz = sizeof(metricdesc) / sizeof(metricdesc[0]); + +static int +windows_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + windows_instance_refresh(indom); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +windows_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + windows_fetch_refresh(numpmid, pmidlist, pmda); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static pdh_value_t * +find_instance_value(unsigned int item, unsigned int inst) +{ + pdh_metric_t *mp = &metricdesc[item]; + int i; + + /* fast check for direct mapped instance ID */ + if (inst < mp->num_vals && mp->vals[inst].inst == inst) + return (mp->vals[inst].flags & V_COLLECTED) ? &mp->vals[inst] : NULL; + + /* scan iteratively through instance IDs looking for this one */ + for (i = 0; i < mp->num_vals; i++) { + if (mp->vals[i].inst != inst) + continue; + if (!(mp->vals[i].flags & V_COLLECTED)) + break; + return &mp->vals[i]; + } + return NULL; +} + +static int +filesys_fetch_callback(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + pdh_value_t *vp; + unsigned long long used, avail, capacity; + float used_space, free_space, free_percent; + + /* + * Special case handling for the derived filesystem metrics + * which map the PDH services semantics for some metrics to + * the usual metrics from other platforms. + * 67 filesys.full + * 117 filesys.capacity + * 118 filesys.used + * 119 filesys.free + * 120 dummy metric, metricdesc holds FreeMB + * 121 dummy metric, metricdesc holds %Free + */ + if (item == 67) { /* filesys.full, metricdesc holds %Free */ + vp = find_instance_value(item, inst); + if (!vp) + return 0; + atom->f = (1.0 - vp->atom.f) * 100.0; + return 1; + } + + vp = find_instance_value(120,inst); /* dummy, metricdesc holds FreeMB */ + if (!vp) + return 0; + free_space = ((float)vp->atom.ul); + + vp = find_instance_value(121,inst); /* dummy, metricdesc holds %Free */ + if (!vp) + return 0; + free_percent = vp->atom.f; + + used_space = (free_space / free_percent) - free_space; + used = 1024 * (unsigned long long)used_space; /* MB to KB */ + avail = 1024 * (unsigned long long)free_space; /* MB to KB */ + capacity = used + avail; + + if (item == 117) /* filesys.capacity */ + atom->ull = capacity; + else if (item == 118) /* filesys.used */ + atom->ull = used; + else if (item == 119) /* filesys.free */ + atom->ull = avail; + return 1; +} + +static int +network_fetch_callback(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + pdh_value_t *vp; + + /* + * Special case handling for the derived network bandwidth metrics + * 235 network.interface.bandwidth (base, no derived) + * 236 network.interface.speed (mbytes, float) + * 237 network.interface.baudrate (in bytes) + */ + vp = find_instance_value(235,inst); + if (!vp) + return 0; + if (item == 236) + atom->f = ((float)vp->atom.ull / 8 / 1024 / 1024); + else if (item == 237) + atom->ul = (vp->atom.ull / 8); + return 1; +} + +static int +memstat_fetch_callback(unsigned int item, unsigned int inst, pmAtomValue *atom) +{ + if (inst == PM_INDOM_NULL) { + switch (item) { + case 0: /* mem.physmem */ + atom->ull = windows_memstat.ullTotalPhys / 1024; + return 1; + case 1: /* mem.freemem */ + case 4: /* mem.util.free */ + atom->ull = windows_memstat.ullAvailPhys / 1024; + return 1; + case 2: /* mem.util.load */ + atom->ul = windows_memstat.dwMemoryLoad; + return 1; + case 3: /* mem.util.used */ + atom->ull = windows_memstat.ullTotalPhys; + atom->ull =- windows_memstat.ullAvailPhys; + atom->ull /= 1024; + return 1; + case 5: /* swap.length */ + atom->ull = windows_memstat.ullTotalPageFile / 1024; + return 1; + case 6: /* swap.used */ + atom->ull = windows_memstat.ullTotalPageFile; + atom->ull -= windows_memstat.ullAvailPageFile; + atom->ull /= 1024; + return 1; + case 7: /* swap.free */ + atom->ull = windows_memstat.ullAvailPageFile / 1024; + return 1; + } + } + return 0; +} + +static int +windows_fetch_callback(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *pmidp = (__pmID_int *)&mdesc->m_desc.pmid; + pdh_value_t *vp; + + if (pmidp->cluster == 1) + return memstat_fetch_callback(pmidp->item, inst, atom); + + if (pmidp->cluster != 0 || pmidp->item > metricdesc_sz || + (pmidp->item == 120 || pmidp->item == 121)) /* dummies */ + return PM_ERR_PMID; + + /* + * Check if its one of the derived metrics, or one that doesn't use PDH + */ + switch (pmidp->item) { + case 106: /* hinv.physmem */ + atom->ul = (windows_memstat.ullTotalPhys / (1024 * 1024)); + return 1; + case 107: /* hinv.ncpu */ + atom->ul = pmdaCacheOp(INDOM(pmidp->domain, CPU_INDOM), + PMDA_CACHE_SIZE_ACTIVE); + return 1; + case 108: /* hinv.ndisk */ + atom->ul = pmdaCacheOp(INDOM(pmidp->domain, DISK_INDOM), + PMDA_CACHE_SIZE_ACTIVE); + return 1; + case 109: /* kernel.uname.distro */ + atom->cp = windows_uname; + return 1; + case 110: /* kernel.uname.release */ + atom->cp = windows_build; + return 1; + case 111: /* kernel.uname.version */ + atom->cp = windows_build; + return 1; + case 112: /* kernel.uname.sysname */ + atom->cp = "Windows"; + return 1; + case 113: /* kernel.uname.machine */ + atom->cp = windows_machine; + return 1; + case 114: /* kernel.uname.nodename */ + atom->cp = "?"; + return 1; + case 115: /* pmda.uname */ + atom->cp = windows_uname; + return 1; + case 116: /* pmda.version */ + atom->cp = pmGetConfig("PCP_VERSION"); + return 1; + case 67: case 117: case 118: case 119: + return filesys_fetch_callback(pmidp->item, inst, atom); + case 232: /* hinv.nfilesys */ + atom->ul = pmdaCacheOp(INDOM(pmidp->domain, FILESYS_INDOM), + PMDA_CACHE_SIZE_ACTIVE); + return 1; + case 233: /* hinv.pagesize */ + atom->ul = windows_pagesize; + return 1; + case 236: case 237: + return network_fetch_callback(pmidp->item, inst, atom); + } + + /* + * All other (most) metrics will go through this path + */ + vp = find_instance_value(pmidp->item, inst); + if (!vp) + return 0; + *atom = vp->atom; + return 1; +} + +/* + * Initialise the agent. + */ +void +windows_init(pmdaInterface *dp) +{ + static pmdaMetric *metrictab; + char helppath[MAXPATHLEN]; + int metrictab_sz = metricdesc_sz; + int i, sep = __pmPathSeparator(); + + snprintf(helppath, sizeof(helppath), "%s%c" "windows" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_3, "windows DSO", helppath); + if (dp->status != 0) + return; + + /* Create the PMDA's metrictab[] version of the per-metric table */ + metrictab = (pmdaMetric *)malloc(metrictab_sz * sizeof(pmdaMetric)); + if (metrictab == NULL) { + fprintf(stderr, "Error: malloc metrictab [%d] failed: %s\n", + metrictab_sz * sizeof(pmdaMetric), osstrerror()); + return; + } + + /* rewrite pmid & indom, now that we know what the domain number is */ + for (i = 0; i < metrictab_sz; i++) { + pdh_metric_t *mp = &metricdesc[i]; + pmID pmid = mp->desc.pmid; + mp->desc.pmid = pmid_build(dp->domain, pmid_cluster(pmid), pmid_item(pmid)); + if (mp->desc.indom != PM_INDOM_NULL) + mp->desc.indom = INDOM(dp->domain, mp->desc.indom); + } + + windows_open(dp->domain); + + /* write the metrictab entry for this metric, descriptor now setup */ + for (i = 0; i < metrictab_sz; i++) { + metrictab[i].m_desc = metricdesc[i].desc; + metrictab[i].m_user = NULL; + } + + dp->version.two.fetch = windows_fetch; + dp->version.two.instance = windows_instance; + dp->version.two.text = windows_help; + pmdaSetFetchCallBack(dp, windows_fetch_callback); + pmdaInit(dp, NULL, 0, metrictab, metrictab_sz); +} diff --git a/src/pmdas/windows/pmns.disk b/src/pmdas/windows/pmns.disk new file mode 100644 index 0000000..9849740 --- /dev/null +++ b/src/pmdas/windows/pmns.disk @@ -0,0 +1,52 @@ +disk { + all + dev +} + +disk.all { + read WINDOWS:0:15 + write WINDOWS:0:16 + total WINDOWS:0:17 + split_io WINDOWS:0:132 + read_bytes WINDOWS:0:18 + write_bytes WINDOWS:0:19 + total_bytes WINDOWS:0:20 + read_time WINDOWS:0:214 + write_time WINDOWS:0:215 + total_time WINDOWS:0:216 + average +} + +disk.dev { + read WINDOWS:0:21 + write WINDOWS:0:22 + total WINDOWS:0:23 + split_io WINDOWS:0:133 + read_bytes WINDOWS:0:24 + write_bytes WINDOWS:0:25 + total_bytes WINDOWS:0:26 + idle WINDOWS:0:68 + queue_len WINDOWS:0:101 + read_time WINDOWS:0:217 + write_time WINDOWS:0:218 + total_time WINDOWS:0:219 + average +} + +disk.all.average { + read_bytes WINDOWS:0:220 + write_bytes WINDOWS:0:221 + total_bytes WINDOWS:0:222 + read_time WINDOWS:0:223 + write_time WINDOWS:0:224 + total_time WINDOWS:0:225 +} + +disk.dev.average { + read_bytes WINDOWS:0:226 + write_bytes WINDOWS:0:227 + total_bytes WINDOWS:0:228 + read_time WINDOWS:0:229 + write_time WINDOWS:0:230 + total_time WINDOWS:0:231 +} diff --git a/src/pmdas/windows/pmns.filesys b/src/pmdas/windows/pmns.filesys new file mode 100644 index 0000000..20137c3 --- /dev/null +++ b/src/pmdas/windows/pmns.filesys @@ -0,0 +1,6 @@ +filesys { + full WINDOWS:0:67 + capacity WINDOWS:0:117 + used WINDOWS:0:118 + free WINDOWS:0:119 +} diff --git a/src/pmdas/windows/pmns.hinv b/src/pmdas/windows/pmns.hinv new file mode 100644 index 0000000..d5cd0d0 --- /dev/null +++ b/src/pmdas/windows/pmns.hinv @@ -0,0 +1,7 @@ +hinv { + physmem WINDOWS:0:106 + ncpu WINDOWS:0:107 + ndisk WINDOWS:0:108 + nfilesys WINDOWS:0:232 + pagesize WINDOWS:0:233 +} diff --git a/src/pmdas/windows/pmns.kernel b/src/pmdas/windows/pmns.kernel new file mode 100644 index 0000000..631e397 --- /dev/null +++ b/src/pmdas/windows/pmns.kernel @@ -0,0 +1,48 @@ +kernel { + all + percpu + num_processes WINDOWS:0:8 + num_threads WINDOWS:0:9 + uname +} + +kernel.all { + cpu + file + pswitch WINDOWS:0:10 + uptime WINDOWS:0:234 +} + +kernel.all.cpu { + user WINDOWS:0:0 + idle WINDOWS:0:1 + sys WINDOWS:0:2 + intr WINDOWS:0:3 +} + +kernel.percpu { + cpu +} + +kernel.percpu.cpu { + user WINDOWS:0:4 + idle WINDOWS:0:5 + sys WINDOWS:0:6 + intr WINDOWS:0:7 +} + +kernel.all.file { + read WINDOWS:0:11 + write WINDOWS:0:12 + read_bytes WINDOWS:0:13 + write_bytes WINDOWS:0:14 +} + +kernel.uname { + distro WINDOWS:0:109 + release WINDOWS:0:110 + version WINDOWS:0:111 + sysname WINDOWS:0:112 + machine WINDOWS:0:113 + nodename WINDOWS:0:114 +} diff --git a/src/pmdas/windows/pmns.mem b/src/pmdas/windows/pmns.mem new file mode 100644 index 0000000..be71ec6 --- /dev/null +++ b/src/pmdas/windows/pmns.mem @@ -0,0 +1,61 @@ +mem { + page_faults WINDOWS:0:27 + available WINDOWS:0:28 + committed_bytes WINDOWS:0:29 + pool + cache + commit_limit WINDOWS:0:136 + write_copies WINDOWS:0:137 + transition_faults WINDOWS:0:138 + demand_zero_faults WINDOWS:0:140 + pages_total WINDOWS:0:141 + page_reads WINDOWS:0:142 + pages_output WINDOWS:0:143 + page_writes WINDOWS:0:144 + system + physmem WINDOWS:1:0 + freemem WINDOWS:1:1 + util +} + +mem.util { + load WINDOWS:1:2 + used WINDOWS:1:3 + free WINDOWS:1:4 +} + +swap { + length WINDOWS:1:5 + used WINDOWS:1:6 + free WINDOWS:1:7 +} + +mem.pool { + paged_bytes WINDOWS:0:30 + non_paged_bytes WINDOWS:0:31 + paged_allocs WINDOWS:0:145 + nonpaged_allocs WINDOWS:0:146 + paged_resident_bytes WINDOWS:0:150 +} + +mem.cache { + read_ahead WINDOWS:0:35 + lazy_writes WINDOWS:0:32 + lazy_write_pages WINDOWS:0:33 + mdl + faults WINDOWS:0:139 + bytes WINDOWS:0:148 + bytes_peak WINDOWS:0:149 +} + +mem.cache.mdl { + read WINDOWS:0:34 + sync_read WINDOWS:0:36 + async_read WINDOWS:0:37 +} + +mem.system { + free_ptes WINDOWS:0:147 + total_code_bytes WINDOWS:0:151 + resident_code_bytes WINDOWS:0:152 +} diff --git a/src/pmdas/windows/pmns.network b/src/pmdas/windows/pmns.network new file mode 100644 index 0000000..7439944 --- /dev/null +++ b/src/pmdas/windows/pmns.network @@ -0,0 +1,42 @@ +network { + interface + tcp +} + +network.interface { + in + out + total + bandwidth WINDOWS:0:235 + speed WINDOWS:0:236 + baudrate WINDOWS:0:237 +} + +network.interface.in { + packets WINDOWS:0:38 + bytes WINDOWS:0:39 + errors WINDOWS:0:40 +} + +network.interface.out { + packets WINDOWS:0:41 + bytes WINDOWS:0:42 + errors WINDOWS:0:43 +} + +network.interface.total { + packets WINDOWS:0:44 + bytes WINDOWS:0:45 +} + +network.tcp { + activeopens WINDOWS:0:123 + passiveopens WINDOWS:0:124 + attemptfails WINDOWS:0:125 + estabresets WINDOWS:0:126 + currestab WINDOWS:0:127 + insegs WINDOWS:0:128 + outsegs WINDOWS:0:129 + totalsegs WINDOWS:0:130 + retranssegs WINDOWS:0:131 +} diff --git a/src/pmdas/windows/pmns.pmda b/src/pmdas/windows/pmns.pmda new file mode 100644 index 0000000..2eb3323 --- /dev/null +++ b/src/pmdas/windows/pmns.pmda @@ -0,0 +1,4 @@ +pmda { + uname WINDOWS:0:115 + version WINDOWS:0:116 +} diff --git a/src/pmdas/windows/pmns.process b/src/pmdas/windows/pmns.process new file mode 100644 index 0000000..a03999a --- /dev/null +++ b/src/pmdas/windows/pmns.process @@ -0,0 +1,59 @@ +process { + count WINDOWS:0:173 + psinfo + memory + io + thread +} + +process.psinfo { + pid WINDOWS:0:174 + ppid WINDOWS:0:175 + cpu_time WINDOWS:0:176 + elapsed_time WINDOWS:0:177 + utime WINDOWS:0:178 + stime WINDOWS:0:179 + nthreads WINDOWS:0:180 + priority_base WINDOWS:0:181 + nhandles WINDOWS:0:182 + page_faults WINDOWS:0:183 +} + +process.memory { + size WINDOWS:0:184 + rss WINDOWS:0:185 + rss_peak WINDOWS:0:186 + virtual WINDOWS:0:187 + virtual_peak WINDOWS:0:188 + page_file WINDOWS:0:189 + page_file_peak WINDOWS:0:190 + private WINDOWS:0:191 + pool_paged WINDOWS:0:192 + pool_nonpaged WINDOWS:0:193 +} + +process.io { + reads WINDOWS:0:194 + writes WINDOWS:0:195 + data WINDOWS:0:196 + other WINDOWS:0:197 + read_bytes WINDOWS:0:198 + write_bytes WINDOWS:0:199 + data_bytes WINDOWS:0:200 + other_bytes WINDOWS:0:201 +} + +process.thread { + context_switches WINDOWS:0:202 + cpu_time WINDOWS:0:203 + utime WINDOWS:0:204 + stime WINDOWS:0:205 + elapsed_time WINDOWS:0:206 + priority WINDOWS:0:207 + priority_base WINDOWS:0:208 + start_address WINDOWS:0:209 + state WINDOWS:0:210 + wait_reason WINDOWS:0:211 + process_id WINDOWS:0:212 + thread_id WINDOWS:0:213 +} diff --git a/src/pmdas/windows/pmns.sqlserver b/src/pmdas/windows/pmns.sqlserver new file mode 100644 index 0000000..ac29095 --- /dev/null +++ b/src/pmdas/windows/pmns.sqlserver @@ -0,0 +1,147 @@ +sqlserver { + access + buf_mgr + cache_mgr + databases + latches + locks + sql + connections WINDOWS:0:83 + mem_mgr + user_settable +} + +sqlserver.buf_mgr { + cache_hit_ratio WINDOWS:0:46 + page_lookups WINDOWS:0:47 + free_list_stalls WINDOWS:0:48 + free_pages WINDOWS:0:49 + total_pages WINDOWS:0:50 + target_pages WINDOWS:0:51 + database_pages WINDOWS:0:52 + reserved_pages WINDOWS:0:53 + stolen_pages WINDOWS:0:54 + lazy_writes WINDOWS:0:55 + readahead_pages WINDOWS:0:56 + procedure_cache_pages WINDOWS:0:57 + page_reads WINDOWS:0:58 + page_writes WINDOWS:0:59 + checkpoint_pages WINDOWS:0:60 + awe + page_life_expectancy WINDOWS:0:66 +} + +sqlserver.buf_mgr.awe { + lookup_maps WINDOWS:0:61 + stolen_maps WINDOWS:0:62 + write_maps WINDOWS:0:63 + unmap_calls WINDOWS:0:64 + unmap_pages WINDOWS:0:65 +} + +sqlserver.locks { + all + region +} + +sqlserver.locks.all { + requests WINDOWS:0:69 + waits WINDOWS:0:70 + deadlocks WINDOWS:0:71 + timeouts WINDOWS:0:72 + wait_time WINDOWS:0:73 + avg_wait_time WINDOWS:0:74 +} + +sqlserver.locks.region { + requests WINDOWS:0:75 + waits WINDOWS:0:76 + deadlocks WINDOWS:0:77 + timeouts WINDOWS:0:78 + wait_time WINDOWS:0:79 + avg_wait_time WINDOWS:0:80 +} + +sqlserver.cache_mgr { + all + cache +} + +sqlserver.cache_mgr.all { + cache_hit_ratio WINDOWS:0:81 + cache_pages WINDOWS:0:167 + cache_object_count WINDOWS:0:168 + cache_use WINDOWS:0:169 +} + +sqlserver.cache_mgr.cache { + cache_hit_ratio WINDOWS:0:82 + cache_pages WINDOWS:0:170 + cache_object_count WINDOWS:0:171 + cache_use WINDOWS:0:172 +} + +sqlserver.databases { + all + db +} + +sqlserver.databases.all { + transactions WINDOWS:0:84 + log_flushes WINDOWS:0:102 + log_bytes_flushed WINDOWS:0:104 + data_file_size WINDOWS:0:90 + log_file_size WINDOWS:0:91 + log_file_used WINDOWS:0:92 + active_transactions WINDOWS:0:134 +} + +sqlserver.databases.db { + transactions WINDOWS:0:85 + log_flushes WINDOWS:0:103 + log_bytes_flushed WINDOWS:0:105 + data_file_size WINDOWS:0:93 + log_file_size WINDOWS:0:94 + log_file_used WINDOWS:0:95 + active_transactions WINDOWS:0:135 +} + +sqlserver.latches { + waits WINDOWS:0:87 + wait_time WINDOWS:0:88 + avg_wait_time WINDOWS:0:89 +} + +sqlserver.sql { + batch_requests WINDOWS:0:86 + compilations WINDOWS:0:96 + re_compilations WINDOWS:0:97 +} + +sqlserver.access { + full_scans WINDOWS:0:98 + pages_allocated WINDOWS:0:99 + table_lock_escalations WINDOWS:0:100 + page_splits WINDOWS:0:122 +} + +sqlserver.mem_mgr { + connection_memory WINDOWS:0:153 + granted_workspace WINDOWS:0:154 + lock_memory WINDOWS:0:155 + lock_blocks_allocated WINDOWS:0:156 + lock_owner_blocks_allocated WINDOWS:0:157 + lock_blocks WINDOWS:0:158 + lock_owner_blocks WINDOWS:0:159 + maximum_workspace_memory WINDOWS:0:160 + memory_grants_outstanding WINDOWS:0:161 + memory_grants_pending WINDOWS:0:162 + optimizer_memory WINDOWS:0:163 + sql_cache_memory WINDOWS:0:164 + target_server_memory WINDOWS:0:165 + total_server_memory WINDOWS:0:166 +} + +sqlserver.user_settable { + query WINDOWS:0:238 +} diff --git a/src/pmdas/windows/root b/src/pmdas/windows/root new file mode 100644 index 0000000..2b4e3dc --- /dev/null +++ b/src/pmdas/windows/root @@ -0,0 +1,29 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include + +root { + hinv + kernel + disk + filesys + mem + swap + network + sqlserver + pmda + process +} + +#include "pmns.hinv" +#include "pmns.kernel" +#include "pmns.disk" +#include "pmns.filesys" +#include "pmns.mem" +#include "pmns.network" +#include "pmns.sqlserver" +#include "pmns.pmda" +#include "pmns.process" + diff --git a/src/pmdas/zimbra/GNUmakefile b/src/pmdas/zimbra/GNUmakefile new file mode 100644 index 0000000..76d69a6 --- /dev/null +++ b/src/pmdas/zimbra/GNUmakefile @@ -0,0 +1,51 @@ +#!gmake +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = zimbra +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +LSRCFILES = Install Remove pmda$(IAM).pl zimbraprobe.sh pmlogconf.all +LDIRT = domain.h root pmns *.log $(MAN_PAGES) + +ifneq ($(POD2MAN),) +MAN_SECTION = 1 +MAN_PAGES = pmda$(IAM).$(MAN_SECTION) +MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION) +endif + +default: check_domain $(MAN_PAGES) + +pmda$(IAM).1: pmda$(IAM).pl + $(POD_MAKERULE) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 755 zimbraprobe.sh $(PMDADIR)/zimbraprobe + $(INSTALL) -m 644 pmda$(IAM).pl $(PMDADIR)/pmda$(IAM).pl + @$(INSTALL_MAN) + $(INSTALL) -m 755 -d $(PCP_VAR_DIR)/config/pmlogconf/$(IAM) + $(INSTALL) -m 644 pmlogconf.all $(PCP_VAR_DIR)/config/pmlogconf/$(IAM)/all + +default_pcp : default + +install_pcp : install + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PERLRULE) diff --git a/src/pmdas/zimbra/Install b/src/pmdas/zimbra/Install new file mode 100755 index 0000000..8bff154 --- /dev/null +++ b/src/pmdas/zimbra/Install @@ -0,0 +1,33 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Zimbra PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=zimbra +perl_opt=true +daemon_opt=false +forced_restart=false + +if ! test -d /opt/zimbra/zmstat ; then + echo "Zimbra does not appear to be installed (no /opt/zimbra/zmstat)" + echo "Pushing on regardless, but no metric values exist initially." +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/zimbra/Remove b/src/pmdas/zimbra/Remove new file mode 100755 index 0000000..beeefe1 --- /dev/null +++ b/src/pmdas/zimbra/Remove @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Zimbra PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=zimbra + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/zimbra/pmdazimbra.pl b/src/pmdas/zimbra/pmdazimbra.pl new file mode 100644 index 0000000..f23c2e6 --- /dev/null +++ b/src/pmdas/zimbra/pmdazimbra.pl @@ -0,0 +1,905 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +use strict; +use warnings; +use PCP::PMDA; + +my $pmda = PCP::PMDA->new('zimbra', 98); +my $probe = pmda_config('PCP_PMDAS_DIR') . '/zimbra/zimbraprobe'; +my $stats = '/opt/zimbra/zmstat/'; + +# Zimbra instrumentation is exported through a series of CSV files. +# Several of these are system-level files that we already have PMDAs +# for, extracted more accurately and efficiently, so we ignore those +# (cpu,vm,mysql). +# For the rest, we use the PMDA.pm file "tail" mechanism to monitor +# appends to each of the CSV files. For all data received, we parse +# the line and extract values. These we store in fixed-size arrays, +# one per metric cluster, then the fetch callback just passes out the +# most recently seen value. + +my ( $imap_domain, $soap_domain ) = ( 0, 1 ); +my @imap_indom = ( + 0 => 'CAPABILITY', 1 => 'UID', 2 => 'STATUS', + 3 => 'SUBSCRIBE', 4 => 'LIST', 5 => 'SELECT', + 6 => 'LOGIN', 7 => 'NAMESPACE', 8 => 'APPEND', + 9 => 'OTHER' ); +my @soap_indom = ( + 0 => 'AuthRequest', 1 => 'DelegateAuthRequest', + 2 => 'GetAccountInfoRequest', 3 => 'GetAllServersRequest', + 4 => 'GetDomainRequest', 5 => 'GetDomainInfoRequest', + 6 => 'GetCosRequest', 7 => 'GetServiceStatusRequest', + 8 => 'GetVersionInfoRequest', 9 => 'GetAccountMembershipRequest', + 10 => 'GetLDAPEntriesRequest', 11 => 'GetAdminSavedSearchesRequest', + 12 => 'GetAdminExtensionZimletsRequest', 13 => 'GetAllConfigRequest', + 14 => 'GetLicenseRequest', 15 => 'SearchDirectoryRequest', + 16 => 'Other' ); + +sub zimbra_array_lookup +{ + my ( $indomref, $name ) = @_; + my @indom = @$indomref; + my $index; + + for ($index = 1; $index < $#indom; $index += 2) { + return (($index-1) / 2) unless ($indom[$index] ne $name); + } + return (($#indom-1) / 2); # return the "other" bucket +} + +use vars qw( @fd_values @imap_time_values @imap_count_values @soap_time_values + @soap_count_values @mailbox_values @mtaqueue_values @proc_values + @threads_values @probe_values ); +use vars qw( $fd_timestamp $imap_timestamp $soap_timestamp + $mailbox_timestamp $mtaqueue_timestamp $proc_timestamp + $threads_timestamp $probe_timestamp ); +$fd_timestamp = $imap_timestamp = $soap_timestamp = $mailbox_timestamp = + $mtaqueue_timestamp = $proc_timestamp = $threads_timestamp = + $probe_timestamp = 0; + +sub zimbra_fd_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_fd_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d), ||) { + chomp; + $fd_timestamp = $1; + @fd_values = split /,/; + } +} + +sub zimbra_imap_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_imap_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d),||) { + chomp; + my $timestamp = $1; + my @values = split /,/; + + if ($timestamp ne $imap_timestamp) { + $imap_timestamp = $timestamp; + @imap_count_values = (); + $imap_count_values[($#imap_indom-1)/2] = 0; + @imap_time_values = (); + $imap_time_values[($#imap_indom-1)/2] = 0; + } + + my $index = zimbra_array_lookup(\@imap_indom, $values[0]); + $imap_count_values[$index] = $values[1]; + $imap_time_values[$index] = $values[2]; + } +} + +sub zimbra_soap_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_soap_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d),||) { + chomp; + my $timestamp = $1; + my @values = split /,/; + + if ($timestamp ne $soap_timestamp) { + $soap_timestamp = $timestamp; + @soap_count_values = (); + $soap_count_values[($#soap_indom-1)/2] = 0; + @soap_time_values = (); + $soap_time_values[($#soap_indom-1)/2] = 0; + } + + my $index = zimbra_array_lookup(\@soap_indom, $values[0]); + $soap_count_values[$index] = $values[1]; + $soap_time_values[$index] = $values[2]; + } +} + +sub zimbra_mailbox_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_mailbox_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d),||) { + chomp; + $mailbox_timestamp = $1; + @mailbox_values = split /,/; + } +} + +sub zimbra_mtaqueue_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_mtaqueue_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d), ||) { + chomp; + $mtaqueue_timestamp = $1; + @mtaqueue_values = split /,/; + } +} + +sub zimbra_proc_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_proc_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d), ||) { + chomp; + $proc_timestamp = $1; + @proc_values = (); + my @values = split /,/; + splice(@values, 0, 5); # ditch "system" values + + # 7 values for mailbox,mysql,convertd,ldap,postfix,amavis,clam + # seems sometimes not all are installed/available/whatever, so + # take care to handle that case. + + while ($#values >= 7) { + my $offset = 0; + my @chunk = splice(@values, 0, 8); + if (defined($chunk[0])) { + $chunk[0] =~ s/\s//g; + } + if (!defined($chunk[0])) { + $pmda->log("zimbra_proc_parser unexpected input: $_"); + } + elsif ($chunk[0] eq 'mailbox') { $offset = 0; } + elsif ($chunk[0] eq 'mysql') { $offset = 7; } + elsif ($chunk[0] eq 'convertd') { $offset = 14; } + elsif ($chunk[0] eq 'ldap') { $offset = 21; } + elsif ($chunk[0] eq 'postfix') { $offset = 28; } + elsif ($chunk[0] eq 'amavis') { $offset = 35; } + elsif ($chunk[0] eq 'clam') { $offset = 42; } + else { + $pmda->log("zimbra_proc_parser unexpected data: $chunk[0]"); + } + shift @chunk; # remove the chunk name - then add values + splice(@proc_values, $offset, 7, @chunk); # to correct spot + } + } + else { + # This includes the initial header line, so just debug: + # $pmda->log("zimbra_proc_parser could not parse line: $_"); + } +} + +sub zimbra_threads_parser +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_threads_parser got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d),||) { + chomp; + $threads_timestamp = $1; + @threads_values = split /,/; + } +} + +sub zimbra_probe_callback +{ + ( undef, $_ ) = @_; + + # $pmda->log("zimbra_probe_callback got line: $_"); + if (s|^(\d\d/\d\d/\d\d\d\d \d\d:\d\d:\d\d)$||) { + $probe_timestamp = $1; + } else { + my ($service, $status) = split; + + return unless defined($status); + + $status = ($status eq 'Running') ? 1 : 0; + $probe_values[ 0] = $status unless ($service ne 'antivirus'); + $probe_values[ 1] = $status unless ($service ne 'antispam'); + $probe_values[ 2] = $status unless ($service ne 'archiving'); + $probe_values[ 3] = $status unless ($service ne 'convertd'); + $probe_values[ 4] = $status unless ($service ne 'mta'); + $probe_values[ 5] = $status unless ($service ne 'mailbox'); + $probe_values[ 6] = $status unless ($service ne 'logger'); + $probe_values[ 7] = $status unless ($service ne 'snmp'); + $probe_values[ 8] = $status unless ($service ne 'ldap'); + $probe_values[ 9] = $status unless ($service ne 'spell'); + $probe_values[10] = $status unless ($service ne 'imapproxy'); + $probe_values[11] = $status unless ($service ne 'stats'); + } +} + +sub zimbra_fetch_callback +{ + my ($cluster, $item, $inst) = @_; + + #$pmda->log("zimbra_fetch_callback for PMID: $cluster.$item ($inst)"); + if ($inst != PM_IN_NULL && $cluster != 1 && $cluster != 2) { + return (PM_ERR_INST, 0); + } + + if ($cluster == 0) { # fd.csv + if ($item >= 0 && $item <= 0) { + return (PM_ERR_AGAIN, 0) unless defined($fd_values[$item]); + return ($fd_values[$item], 1); + } + } + elsif ($cluster == 1 && $item == 0) { # imap.csv + if ($inst >= 0 && $inst <= $#imap_indom) { + return (PM_ERR_AGAIN, 0) unless defined($imap_count_values[$inst]); + return ($imap_count_values[$inst], 1); + } + } + elsif ($cluster == 1 && $item == 1) { # imap.csv + if ($inst >= 0 && $inst < $#imap_indom) { + return (PM_ERR_AGAIN, 0) unless defined($imap_time_values[$inst]); + return ($imap_time_values[$inst], 1); + } + } + elsif ($cluster == 2 && $item == 0) { # soap.csv + if ($inst >= 0 && $inst < $#soap_indom) { + return (PM_ERR_AGAIN, 0) unless defined($soap_count_values[$inst]); + return ($soap_count_values[$inst], 1); + } + } + elsif ($cluster == 2 && $item == 1) { # soap.csv + if ($inst >= 0 && $inst < $#soap_indom) { + return (PM_ERR_AGAIN, 0) unless defined($soap_time_values[$inst]); + return ($soap_time_values[$inst], 1); + } + } + elsif ($cluster == 3) { # mailboxd.csv + if ($item >= 0 && $item <= 60) { + return (PM_ERR_AGAIN, 0) unless defined($mailbox_values[$item]); + if ($mailbox_values[$item] eq '') { + $mailbox_values[$item] = 0; + } + return ($mailbox_values[$item], 1); + } + } + elsif ($cluster == 4) { # mtaqueue.csv + if ($item >= 0 && $item <= 1) { + return (PM_ERR_AGAIN, 0) unless defined($mtaqueue_values[$item]); + return ($mtaqueue_values[$item], 1); + } + } + elsif ($cluster == 5) { # proc.csv + if ($item >= 0 && $item <= 48) { + return (PM_ERR_AGAIN, 0) unless defined($proc_values[$item]); + return ($proc_values[$item], 1); + } + } + elsif ($cluster == 6) { # threads.csv + if ($item >= 0 && $item <= 13) { + return (PM_ERR_AGAIN, 0) unless defined($threads_values[$item]); + return ($threads_values[$item], 1); + } + } + elsif ($cluster == 7) { # timestamps + return ($fd_timestamp, 1) unless ($item != 0); + return ($imap_timestamp, 1) unless ($item != 1); + return ($soap_timestamp, 1) unless ($item != 2); + return ($mailbox_timestamp, 1) unless ($item != 3); + return ($mtaqueue_timestamp, 1) unless ($item != 4); + return ($proc_timestamp, 1) unless ($item != 5); + return ($threads_timestamp, 1) unless ($item != 6); + return ($probe_timestamp, 1) unless ($item != 7); + } + elsif ($cluster == 8) { # status probe + if ($item >= 0 && $item <= 11) { + return (PM_ERR_APPVERSION, 0) unless defined($probe_values[$item]); + return ($probe_values[$item], 1); + } + } + + return (PM_ERR_PMID, 0); +} + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.fd_count', + '/opt/zimbra/zmstat/fd.csv', 'Open file descriptors'); + +$pmda->add_metric(pmda_pmid(1,0), PM_TYPE_U32, $imap_domain, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.imap.count', + '/opt/zimbra/zmstat/imap.csv', + 'Count of Internet Message Access Protocol commands'); +$pmda->add_metric(pmda_pmid(1,1), PM_TYPE_U32, $imap_domain, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.imap.avgtime', + '/opt/zimbra/zmstat/imap.csv', + 'Time spent executing Internet Message Access Protocol commands'); + +$pmda->add_metric(pmda_pmid(2,0), PM_TYPE_U32, $soap_domain, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.soap.count', + '/opt/zimbra/zmstat/soap.csv', + 'Count of Simple Object Access Protocol commands'); +$pmda->add_metric(pmda_pmid(2,1), PM_TYPE_U32, $soap_domain, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.soap.avgtime', + '/opt/zimbra/zmstat/soap.csv', + 'Time spent executing Simple Object Access Protocol commands'); + +$pmda->add_metric(pmda_pmid(3,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.lmtp.rcvd_msgs', + '/opt/zimbra/zmstat/mailboxd.csv', 'Received mail messages'); +$pmda->add_metric(pmda_pmid(3,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.lmtp.rcvd_bytes', + '/opt/zimbra/zmstat/mailboxd.csv', 'Received bytes'); +$pmda->add_metric(pmda_pmid(3,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.lmtp.rcvd_rcpt', + '/opt/zimbra/zmstat/mailboxd.csv', 'Received receipts'); +$pmda->add_metric(pmda_pmid(3,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.lmtp.dlvd_msgs', + '/opt/zimbra/zmstat/mailboxd.csv', 'Delivered messages'); +$pmda->add_metric(pmda_pmid(3,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.lmtp.dlvd_bytes', + '/opt/zimbra/zmstat/mailboxd.csv', 'Delivered bytes'); +$pmda->add_metric(pmda_pmid(3,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.db_conn.count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Database connection count'); +$pmda->add_metric(pmda_pmid(3,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.db_conn.time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Database connection time'); +$pmda->add_metric(pmda_pmid(3,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.ldap.dc_count', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.ldap.dc_time', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.mbox.add_msg_count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox message add count'); +$pmda->add_metric(pmda_pmid(3,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.mbox.add_msg_time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox message add average time'); +$pmda->add_metric(pmda_pmid(3,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.mbox.get_count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox get count'); +$pmda->add_metric(pmda_pmid(3,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.mbox.get_time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox average get time'); +$pmda->add_metric(pmda_pmid(3,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.mbox_cache', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox cache'); +$pmda->add_metric(pmda_pmid(3,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.mbox_msg_cache', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox message cache'); +$pmda->add_metric(pmda_pmid(3,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.mbox_item_cache', + '/opt/zimbra/zmstat/mailboxd.csv', 'Mailbox item cache'); +$pmda->add_metric(pmda_pmid(3,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.soap.count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Simple Object Access Protocol count'); +$pmda->add_metric(pmda_pmid(3,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.soap.time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Simple Object Access Protocol time'); +$pmda->add_metric(pmda_pmid(3,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.imap.count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Internet Message Access Protocol count'); +$pmda->add_metric(pmda_pmid(3,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.imap.time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Internet Message Access Protocol time'); +$pmda->add_metric(pmda_pmid(3,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.pop.count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Post Office Protocol count'); +$pmda->add_metric(pmda_pmid(3,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.pop.time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Post Office Protocol time'); +$pmda->add_metric(pmda_pmid(3,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.idx.wrt_avg', + '/opt/zimbra/zmstat/mailboxd.csv', 'Lucene Index Writer count'); +$pmda->add_metric(pmda_pmid(3,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.idx.wrt_opened', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.idx.wrt_opened_cache_hit', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,25), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.calcache.hit', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,26), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.calcache.mem_hit', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.calcache.lru_size', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,28), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.idx.bytes_written', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,29), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.idx.bytes_written_avg', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,30), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.idx.bytes_read', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,31), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.idx.bytes_read_avg', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,32), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.bis.read', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,33), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.bis.seek_rate', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.mailboxd.db_pool_size', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,35), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.innodb_bp_hit_rate', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,36), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.pop.conn', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.pop.ssl_conn', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,38), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.imap.conn', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,39), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.imap.ssl_conn', + '/opt/zimbra/zmstat/mailboxd.csv', ''); +$pmda->add_metric(pmda_pmid(3,40), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.soap.sessions', + '/opt/zimbra/zmstat/mailboxd.csv', 'Simple Object Access Protocol session count'); +# Zimbra duplicates four metrics here, so skip four for direct indexing +$pmda->add_metric(pmda_pmid(3,45), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.gc.minor_count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java minor garbage collection count'); +$pmda->add_metric(pmda_pmid(3,46), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.gc.minor_time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java minor garbage collection time'); +$pmda->add_metric(pmda_pmid(3,47), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.mailboxd.gc.major_count', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java major garbage collection count'); +$pmda->add_metric(pmda_pmid(3,48), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_COUNTER, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mailboxd.gc.major_time', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java major garbage collection time'); +$pmda->add_metric(pmda_pmid(3,49), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.code_cache_used', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java code cache space used'); +$pmda->add_metric(pmda_pmid(3,50), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.code_cache_free', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java code cache space free'); +$pmda->add_metric(pmda_pmid(3,51), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.eden_space_used', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java Eden area space used'); +$pmda->add_metric(pmda_pmid(3,52), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.eden_space_free', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java Eden area space free'); +$pmda->add_metric(pmda_pmid(3,53), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.survivor_space_used', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java survivor area space used'); +$pmda->add_metric(pmda_pmid(3,54), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.survivor_space_free', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java survivor area space free'); +$pmda->add_metric(pmda_pmid(3,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.old_gen_space_used', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java old generation space used'); +$pmda->add_metric(pmda_pmid(3,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.old_gen_space_free', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java old generation space free'); +$pmda->add_metric(pmda_pmid(3,57), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.perm_gen_space_used', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java permanent generation space used'); +$pmda->add_metric(pmda_pmid(3,58), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.mempool.perm_gen_space_free', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java permanent generation space free'); +$pmda->add_metric(pmda_pmid(3,59), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.heap.used', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java heap space used'); +$pmda->add_metric(pmda_pmid(3,60), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_BYTE,0,0), 'zimbra.mailboxd.heap.free', + '/opt/zimbra/zmstat/mailboxd.csv', 'Java heap space free'); + +$pmda->add_metric(pmda_pmid(4,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_KBYTE,0,0), 'zimbra.mtaqueue.size', + '/opt/zimbra/zmstat/mtaqueue.csv', + 'Number of kilobytes queued to the Mail Transfer Agent'); +$pmda->add_metric(pmda_pmid(4,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,1,0,0,PM_TIME_MSEC,0), 'zimbra.mtaqueue.requests', + '/opt/zimbra/zmstat/mtaqueue.csv', + 'Number of requests queued to the Mail Transfer Agent'); + +$pmda->add_metric(pmda_pmid(5,0), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.mailbox.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the mailbox process'); +$pmda->add_metric(pmda_pmid(5,1), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.mailbox.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the mailbox process'); +$pmda->add_metric(pmda_pmid(5,2), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.mailbox.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the mailbox process'); +$pmda->add_metric(pmda_pmid(5,3), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.mailbox.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the mailbox processes'); +$pmda->add_metric(pmda_pmid(5,4), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.mailbox.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the mailbox processes'); +$pmda->add_metric(pmda_pmid(5,5), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.mailbox.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the mailbox processes'); +$pmda->add_metric(pmda_pmid(5,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.mailbox.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of mailbox processes'); + +$pmda->add_metric(pmda_pmid(5,7), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.mysql.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the MySQL process'); +$pmda->add_metric(pmda_pmid(5,8), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.mysql.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the MySQL process'); +$pmda->add_metric(pmda_pmid(5,9), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.mysql.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the MySQL process'); +$pmda->add_metric(pmda_pmid(5,10), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.mysql.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the MySQL processes'); +$pmda->add_metric(pmda_pmid(5,11), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.mysql.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the MySQL processes'); +$pmda->add_metric(pmda_pmid(5,12), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.mysql.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the MySQL processes'); +$pmda->add_metric(pmda_pmid(5,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.mysql.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of MySQL processes'); + +$pmda->add_metric(pmda_pmid(5,14), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.convertd.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the convertd process'); +$pmda->add_metric(pmda_pmid(5,15), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.convertd.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the convertd process'); +$pmda->add_metric(pmda_pmid(5,16), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.convertd.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the convertd process'); +$pmda->add_metric(pmda_pmid(5,17), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.convertd.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the convertd processes'); +$pmda->add_metric(pmda_pmid(5,18), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.convertd.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the convertd processes'); +$pmda->add_metric(pmda_pmid(5,19), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.convertd.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the convertd processes'); +$pmda->add_metric(pmda_pmid(5,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.convertd.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of convertd processes'); + +$pmda->add_metric(pmda_pmid(5,21), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.ldap.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the LDAP process'); +$pmda->add_metric(pmda_pmid(5,22), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.ldap.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the LDAP process'); +$pmda->add_metric(pmda_pmid(5,23), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.ldap.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the LDAP process'); +$pmda->add_metric(pmda_pmid(5,24), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.ldap.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the LDAP processes'); +$pmda->add_metric(pmda_pmid(5,25), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.ldap.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the LDAP processes'); +$pmda->add_metric(pmda_pmid(5,26), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.ldap.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the LDAP processes'); +$pmda->add_metric(pmda_pmid(5,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.ldap.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of LDAP processes'); + +$pmda->add_metric(pmda_pmid(5,28), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.postfix.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the postfix process'); +$pmda->add_metric(pmda_pmid(5,29), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.postfix.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the postfix process'); +$pmda->add_metric(pmda_pmid(5,30), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.postfix.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the postfix process'); +$pmda->add_metric(pmda_pmid(5,31), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.postfix.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the postfix processes'); +$pmda->add_metric(pmda_pmid(5,32), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.postfix.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the postfix processes'); +$pmda->add_metric(pmda_pmid(5,33), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.postfix.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the postfix processes'); +$pmda->add_metric(pmda_pmid(5,34), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.postfix.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of postfix processes'); + +$pmda->add_metric(pmda_pmid(5,35), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.amavis.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the virus scanner'); +$pmda->add_metric(pmda_pmid(5,36), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.amavis.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the virus scanner'); +$pmda->add_metric(pmda_pmid(5,37), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.amavis.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the virus scanner'); +$pmda->add_metric(pmda_pmid(5,38), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.amavis.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the virus scanner process'); +$pmda->add_metric(pmda_pmid(5,39), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.amavis.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the virus scanner process'); +$pmda->add_metric(pmda_pmid(5,40), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.amavis.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the virus scanner processes'); +$pmda->add_metric(pmda_pmid(5,41), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.amavis.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of virus scanner processes'); + +$pmda->add_metric(pmda_pmid(5,42), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.clam.cputime', + '/opt/zimbra/zmstat/proc.csv', + 'Total time as a percentage spent executing the anti-virus process'); +$pmda->add_metric(pmda_pmid(5,43), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.clam.utime', + '/opt/zimbra/zmstat/proc.csv', + 'Total user time as a percentage spent executing the anti-virus process'); +$pmda->add_metric(pmda_pmid(5,44), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.proc.clam.stime', + '/opt/zimbra/zmstat/proc.csv', + 'Total systime as a percentage spent executing the anti-virus process'); +$pmda->add_metric(pmda_pmid(5,45), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.clam.total', + '/opt/zimbra/zmstat/proc.csv', + 'Total virtual memory footprint of the anti-virus processes'); +$pmda->add_metric(pmda_pmid(5,46), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.clam.rss', + '/opt/zimbra/zmstat/proc.csv', + 'Resident set size of the clam processes'); +$pmda->add_metric(pmda_pmid(5,47), PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(1,0,0,PM_SPACE_MBYTE,0,0), 'zimbra.proc.clam.shared', + '/opt/zimbra/zmstat/proc.csv', + 'Shared memory space used by the anti-virus processes'); +$pmda->add_metric(pmda_pmid(5,48), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.proc.clam.count', + '/opt/zimbra/zmstat/proc.csv', 'Total number of anti-virus processes'); + +$pmda->add_metric(pmda_pmid(6,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.btpool', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.pool', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.lmtp', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.imap', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.pop3', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.scheduled_tasks', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.timer', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.anonymous_io', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.flap_processor', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.gc', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.socket_acceptor', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.thread', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.other', + '/opt/zimbra/zmstat/threads.csv', ''); +$pmda->add_metric(pmda_pmid(6,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,1,0,0,PM_COUNT_ONE), 'zimbra.threads.total', + '/opt/zimbra/zmstat/threads.csv', ''); + +$pmda->add_metric(pmda_pmid(7,0), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.fd.timestamp', + 'Timestamp for completion of last fd value fetch', ''); +$pmda->add_metric(pmda_pmid(7,1), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.imap.timestamp', + 'Timestamp for completion of last imap value fetch', ''); +$pmda->add_metric(pmda_pmid(7,2), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.soap.timestamp', + 'Timestamp for completion of last soap value fetch', ''); +$pmda->add_metric(pmda_pmid(7,3), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.mailbox.timestamp', + 'Timestamp for completion of last mailbox value fetch', ''); +$pmda->add_metric(pmda_pmid(7,4), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.mtaqueue.timestamp', + 'Timestamp for completion of last mtaqueue value fetch', ''); +$pmda->add_metric(pmda_pmid(7,5), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.proc.timestamp', + 'Timestamp for completion of last process value fetch', ''); +$pmda->add_metric(pmda_pmid(7,6), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.threads.timestamp', + 'Timestamp for completion of last threads value fetch', ''); +$pmda->add_metric(pmda_pmid(7,7), PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.control.status.timestamp', + 'Timestamp for completion of last status probe', ''); + +$pmda->add_metric(pmda_pmid(8,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.antivirus', + 'Status for Zimbra antivirus service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,1), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.antispam', + 'Status for Zimbra antispam service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.archiving', + 'Status for Zimbra archiving service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.convertd', + 'Status for Zimbra convertd service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.mta', + 'Status for Zimbra MTA service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.mailbox', + 'Status for Zimbra mailbox service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.logger', + 'Status for Zimbra logger service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.snmp', + 'Status for Zimbra SNMP service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.ldap', + 'Status for Zimbra LDAP service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.spell', + 'Status for Zimbra spell service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.imapproxy', + 'Status for Zimbra imapproxy service - 0=stopped, 1=running', ''); +$pmda->add_metric(pmda_pmid(8,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + pmda_units(0,0,0,0,0,0), 'zimbra.status.stats', + 'Status for Zimbra stats service - 0=stopped, 1=running', ''); + +$pmda->add_indom($imap_domain, \@imap_indom, 'IMAP operations', + 'Internet Message Access Protocol operations'); +$pmda->add_indom($soap_domain, \@soap_indom, 'SOAP operation', + 'Simple Object Access Protocol operations'); + +$pmda->add_tail($stats . 'fd.csv', \&zimbra_fd_parser, 0); +$pmda->add_tail($stats . 'imap.csv', \&zimbra_imap_parser, 0); +$pmda->add_tail($stats . 'mailboxd.csv', \&zimbra_mailbox_parser, 0); +$pmda->add_tail($stats . 'mtaqueue.csv', \&zimbra_mtaqueue_parser, 0); +$pmda->add_tail($stats . 'proc.csv', \&zimbra_proc_parser, 0); +$pmda->add_tail($stats . 'soap.csv', \&zimbra_soap_parser, 0); +$pmda->add_tail($stats . 'threads.csv', \&zimbra_threads_parser, 0); + +$pmda->add_pipe($probe, \&zimbra_probe_callback, 0); + +$pmda->set_fetch_callback(\&zimbra_fetch_callback); +$pmda->set_user('pcp'); +$pmda->run; + +=pod + +=head1 NAME + +pmdazimbra - Zimbra Collaboration Suite (ZCS) PMDA + +=head1 DESCRIPTION + +B is a Performance Metrics Domain Agent (PMDA) which +exports metric values from several subsystems of the Zimbra Suite. +Further details on Zimbra can be found at http://www.zimbra.com/. + +=head1 INSTALLATION + +If you want access to the names and values for the zimbra performance +metrics, do the following as root: + + # cd $PCP_PMDAS_DIR/zimbra + # ./Install + +If you want to undo the installation, do the following as root: + + # cd $PCP_PMDAS_DIR/zimbra + # ./Remove + +B is launched by pmcd(1) and should never be executed +directly. The Install and Remove scripts notify pmcd(1) when +the agent is installed or removed. + +=head1 FILES + +=over + +=item /opt/zimbra/zmstat/* + +comma-separated value files containing Zimbra performance data + +=item $PCP_PMDAS_DIR/zimbra/Install + +installation script for the B agent + +=item $PCP_PMDAS_DIR/zimbra/Remove + +undo installation script for the B agent + +=item $PCP_LOG_DIR/pmcd/zimbra.log + +default log file for error messages from B + +=back + +=head1 SEE ALSO + +pmcd(1). diff --git a/src/pmdas/zimbra/pmlogconf.all b/src/pmdas/zimbra/pmlogconf.all new file mode 100644 index 0000000..3c3f14d --- /dev/null +++ b/src/pmdas/zimbra/pmlogconf.all @@ -0,0 +1,4 @@ +#pmlogconf-setup 2.0 +ident All Zimbra information +probe zimbra.fd_count + zimbra diff --git a/src/pmdas/zimbra/zimbraprobe.sh b/src/pmdas/zimbra/zimbraprobe.sh new file mode 100755 index 0000000..a238e27 --- /dev/null +++ b/src/pmdas/zimbra/zimbraprobe.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# +# Copyright (c) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +delay=${1:-10} + +# First one produces any errors to PMDA logfile +su -c 'zmcontrol status' - zimbra + +while true +do + date +'%d/%m/%Y %H:%M:%S' + sleep $delay + su -c 'zmcontrol status' - zimbra 2>/dev/null +done diff --git a/src/pmdas/zswap/GNUmakefile b/src/pmdas/zswap/GNUmakefile new file mode 100644 index 0000000..e36bfa8 --- /dev/null +++ b/src/pmdas/zswap/GNUmakefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = zswap +PYSCRIPT = pmda$(IAM).python +LSRCFILES = Install Remove $(PYSCRIPT) + +DOMAIN = ZSWAP +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log + +default_pcp default: check_domain + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(PYSCRIPT) $(PMDADIR)/$(PYSCRIPT) + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PYTHONRULE) diff --git a/src/pmdas/zswap/Install b/src/pmdas/zswap/Install new file mode 100644 index 0000000..0ba70c2 --- /dev/null +++ b/src/pmdas/zswap/Install @@ -0,0 +1,28 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the zswap PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=zswap +python_opt=true +daemon_opt=false +forced_restart=true + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/zswap/Remove b/src/pmdas/zswap/Remove new file mode 100644 index 0000000..c85a578 --- /dev/null +++ b/src/pmdas/zswap/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Linux zswap PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=zswap + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/zswap/pmdazswap.python b/src/pmdas/zswap/pmdazswap.python new file mode 100644 index 0000000..ef06d21 --- /dev/null +++ b/src/pmdas/zswap/pmdazswap.python @@ -0,0 +1,189 @@ +''' +Performance Metrics Domain Agent exporting Linux compressed swap metrics. +''' +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +import cpmapi as c_api +from pcp.pmapi import pmUnits +from pcp.pmda import PMDA, pmdaMetric, pmdaIndom +from resource import getpagesize +from os import getenv, listdir + +ZSWAP_PAGESIZE = getpagesize() +ZSWAP_STATS_PATH = '/sys/kernel/debug/zswap' + +class ZswapPMDA(PMDA): + ''' + Performance Metrics Domain Agent exporting compressed swap metrics. + Install it and make basic use of it if you use zswap, as follows: + + # $PCP_PMDAS_DIR/zswap/Install + $ pminfo -fmdtT zswap + ''' + + def zswap_fetch_single(self, name): + ''' Extract one value from a single zswap statistics file. ''' + fullpath = self.path + '/' + name + try: + with open(fullpath) as file: + value = file.readline() + self.values[name] = long(value) + except: + # use a simple approach to avoiding errors on every fetch + if self.fileerrors < self.nmetrics: + self.err("fetch_single: failed to read %s" % fullpath) + self.fileerrors += 1 + pass + + def zswap_fetch(self): + ''' + Called once per PCP "fetch" PDU from pmcd(1) + Iterates over the (sysfs debug) path holding zswap statistics, + building a hash of values (one value extracted from each file). + ''' + # self.log("fetch: %s" % self.path) + try: + self.patherrors = 0 + files = listdir(self.path) + for name in files: + self.zswap_fetch_single(name) + except: + # the kernel module may simply not be loaded currently + self.patherrors = 1 + pass + + + def zswap_fetch_callback(self, cluster, item, inst): + ''' + Main fetch callback - looks up value associated with requested PMID + (from zswap_fetch), converts units if needed, then returns a list of + value,status (single pair) for the requested pmid + ''' + # self.log("fetch callback for %d.%d[%d]" % (cluster, item, inst)) + if inst != c_api.PM_IN_NULL: + return [c_api.PM_ERR_INST, 0] + elif cluster != 0: + return [c_api.PM_ERR_PMID, 0] + if item >= 0 and item < self.nmetrics and self.patherrors == 1: + return [c_api.PM_ERR_AGAIN, 0] + + if item == 0: + return [self.values['pool_limit_hit'], 1] + elif item == 1: + return [self.values['reject_reclaim_fail'], 1] + elif item == 2: + return [self.values['reject_alloc_fail'], 1] + elif item == 3: + return [self.values['reject_kmemcache_fail'], 1] + elif item == 4: + return [self.values['reject_compress_poor'], 1] + elif item == 5: + return [self.values['written_back_pages'] * self.pagesize, 1] + elif item == 6: + return [self.values['duplicate_entry'], 1] + elif item == 7: + return [self.values['pool_pages'] * self.pagesize, 1] + elif item == 8: + return [self.values['stored_pages'] * self.pagesize, 1] + return [c_api.PM_ERR_PMID, 0] + + + def setup_zswap_metrics(self, name): + ''' + Setup the metric table - ensure a values hash entry is setup for + each metric; that way we don't need to worry about KeyErrors in + the fetch callback routine (e.g. if the kernel interface changes). + ''' + self.values['pool_limit_hit'] = 0 + self.add_metric(name + '.pool_limit_hit', pmdaMetric( + self.pmid(0, 0), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE)), + 'Pool limit was hit (see zswap_max_pool_percent module parameter)') + + self.values['reject_reclaim_fail'] = 0 + self.add_metric(name + '.reject_reclaim_fail', pmdaMetric( + self.pmid(0, 1), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE)), + 'Store failed due to a reclaim failure after pool limit was reached') + + self.values['reject_alloc_fail'] = 0 + self.add_metric(name + '.reject_alloc_fail', pmdaMetric( + self.pmid(0, 2), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE)), + 'Store failed because underlying allocator could not get memory') + + self.values['reject_kmemcache_fail'] = 0 + self.add_metric(name + '.reject_kmemcache_fail', pmdaMetric( + self.pmid(0, 3), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE)), + 'Store failed because the entry metadata could not be allocated (rare)') + + self.values['reject_compress_poor'] = 0 + self.add_metric(name + '.reject_compress_poor', pmdaMetric( + self.pmid(0, 4), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE)), + 'Compressed page was too big for the allocator to (optimally) store') + + self.values['written_back_pages'] = 0 + self.add_metric(name + '.written_back_pages', pmdaMetric( + self.pmid(0, 5), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(1, 0, 0, c_api.PM_SPACE_KBYTE, 0, 0)), + 'Pages written back when pool limit was reached') + + self.values['duplicate_entry'] = 0 + self.add_metric(name + '.duplicate_entry', pmdaMetric( + self.pmid(0, 6), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(0, 0, 1, 0, 0, c_api.PM_COUNT_ONE)), + 'Duplicate store was encountered (rare)') + + self.values['pool_pages'] = 0 + self.add_metric(name + '.pool_pages', pmdaMetric( + self.pmid(0, 7), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(1, 0, 0, c_api.PM_SPACE_KBYTE, 0, 0)), + 'Memory pages used by the compressed pool') + + self.values['stored_pages'] = 0 + self.add_metric(name + '.stored_pages', pmdaMetric( + self.pmid(0, 8), + c_api.PM_TYPE_U64, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, + pmUnits(1, 0, 0, c_api.PM_SPACE_KBYTE, 0, 0)), + 'Compressed pages currently stored in zswap') + + + def __init__(self, name, domain): + PMDA.__init__(self, name, domain) + self.patherrors = 0 + self.fileerrors = 0 + self.pagesize = int(getenv('ZSWAP_PAGESIZE', ZSWAP_PAGESIZE)) + self.path = str(getenv('ZSWAP_STATS_PATH', ZSWAP_STATS_PATH)) + + self.values = {} + self.setup_zswap_metrics(name) + self.nmetrics = len(self.values) + + self.set_fetch(self.zswap_fetch) + self.set_fetch_callback(self.zswap_fetch_callback) + + +if __name__ == '__main__': + ZswapPMDA('zswap', 125).run() -- cgit v1.2.3