📄 sadc.c
字号:
/* * sadc: system activity data collector * (C) 1999-2006 by Sebastien GODARD (sysstat <at> wanadoo.fr) * *************************************************************************** * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License, or (at your * * option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without 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 * *************************************************************************** */#include <stdio.h>#include <string.h>#include <stdlib.h>#include <ctype.h>#include <unistd.h>#include <fcntl.h>#include <time.h>#include <errno.h>#include <signal.h>#include <dirent.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/utsname.h>#include "sa.h"#include "common.h"#include "ioconf.h"#ifdef USE_NLS#include <locale.h>#include <libintl.h>#define _(string) gettext(string)#else#define _(string) (string)#endif/* Nb of processors on the machine. A value of 1 means two processors */int cpu_nr = -1;unsigned int serial_nr = 0, iface_nr = 0, irqcpu_nr = 0, disk_nr = 0;int pid_idx = 0, pid_nr = 0;unsigned int sadc_actflag;long interval = 0;int kb_shift = 0;struct file_hdr file_hdr;struct file_stats file_stats;struct stats_one_cpu *st_cpu = NULL;struct stats_serial *st_serial = NULL;struct stats_net_dev *st_net_dev = NULL;struct stats_irq_cpu *st_irq_cpu = NULL;struct disk_stats *st_disk = NULL;struct pid_stats *pid_stats[MAX_PID_NR];unsigned long all_pids[MAX_PID_NR];unsigned char f_pids[MAX_PID_NR];unsigned int interrupts[NR_IRQS];unsigned int u_tmp[NR_DISKS - 1];/* *************************************************************************** * Print usage and exit *************************************************************************** */void usage(char *progname){ /* * Don't show options like -x ALL or -X SELF. * They should only be used with sar. */ fprintf(stderr, _("Usage: %s [ options... ] [ <interval> [ <count> ] ] [ <outfile> ]\n" "Options are:\n" "[ -d ] [ -F ] [ -I ] [ -V ]\n"), progname); exit(1);}/* *************************************************************************** * SIGALRM signal handler *************************************************************************** */void alarm_handler(int sig){ signal(SIGALRM, alarm_handler); alarm(interval);}/* *************************************************************************** * Allocate pid_stats structures *************************************************************************** */void salloc_pid(int nb_pid){ int pid; if ((pid_stats[0] = (struct pid_stats *) malloc(PID_STATS_SIZE * nb_pid)) == NULL) { perror("malloc"); exit(4); } memset(pid_stats[0], 0, PID_STATS_SIZE * nb_pid); for (pid = 1; pid < nb_pid; pid++) /* Structures are aligned but also padded. Thus array members are packed */ pid_stats[pid] = pid_stats[0] + pid; /* Assume nb_pid <= MAX_PID_NR */}/* *************************************************************************** * Display an error message *************************************************************************** */void p_write_error(void){ fprintf(stderr, _("Cannot write data to system activity file: %s\n"), strerror(errno)); exit(2);}/* *************************************************************************** * Init dk_drive* counters (used for sar -b) *************************************************************************** */void init_dk_drive_stat(void){ file_stats.dk_drive = 0; file_stats.dk_drive_rio = file_stats.dk_drive_wio = 0; file_stats.dk_drive_rblk = file_stats.dk_drive_wblk = 0;}/* *************************************************************************** * Insert PID number into the list if not already there and if possible. * Return the index in the list, or MAX_PID_NR if PID could not be inserted. *************************************************************************** */int save_pid(unsigned long pid){ int i = 0; while ((i < pid_idx) && (all_pids[i] != pid)) i++; /* PID not found: insert it if possible */ if ((i == pid_idx) && (pid_idx < MAX_PID_NR)) all_pids[pid_idx++] = pid; return i;}/* *************************************************************************** * Set PID flag value (bit 0 set: -x, bit 1 set: -X) *************************************************************************** */void set_pflag(int child, unsigned long pid){ int i = 0, flag; if (child) flag = X_PPID_SET; else flag = X_PID_SET; if (!pid) { for (i = 0; i < MAX_PID_NR; i++) f_pids[i] |= flag; } else { if ((i = save_pid(pid)) < MAX_PID_NR) f_pids[i] |= flag; }}/* *************************************************************************** * Look for all the PIDs *************************************************************************** */void get_pid_list(void){ DIR *dir; struct dirent *drp; /* Open /proc directory */ if ((dir = opendir(PROC)) == NULL) { perror("opendir"); exit(4); } /* Get directory entries */ while ((drp = readdir(dir)) != NULL) { if (isdigit(drp->d_name[0])) { /* Save PID in the list */ if (save_pid(atol(drp->d_name)) == MAX_PID_NR) break; } } /* Close /proc directory */ closedir(dir);}/* *************************************************************************** * Find number of serial lines that support tx/rx accounting * in /proc/tty/driver/serial file. *************************************************************************** */unsigned int get_serial_lines(void)#ifdef SMP_RACE{ /* * Ignore serial lines if SMP_RACE flag is defined. * This is because there is an SMP race in some 2.2.x kernels that * may be triggered when reading the /proc/tty/driver/serial file. */ return 0;}#else{ FILE *fp; char line[256]; unsigned int sl = 0; if ((fp = fopen(SERIAL, "r")) == NULL) return 0; /* No SERIAL file */ while (fgets(line, 256, fp) != NULL) { /* * tx/rx statistics are always present, * except when serial line is unknown. */ if (strstr(line, "tx:") != NULL) sl++; } fclose(fp); return (sl + NR_SERIAL_PREALLOC);}#endif/* *************************************************************************** * Find number of interfaces (network devices) that are in /proc/net/dev * file *************************************************************************** */unsigned int get_net_dev(void){ FILE *fp; char line[128]; unsigned int dev = 0; if ((fp = fopen(NET_DEV, "r")) == NULL) return 0; /* No network device file */ while (fgets(line, 128, fp) != NULL) { if (strchr(line, ':')) dev++; } fclose(fp); return (dev + NR_IFACE_PREALLOC);}/* *************************************************************************** * Find number of interrupts available per processor (use * /proc/interrupts file). Called on SMP machines only. *************************************************************************** */unsigned int get_irqcpu_nb(unsigned int max_nr_irqcpu){ FILE *fp; char line[256]; unsigned int irq = 0; if ((fp = fopen(INTERRUPTS, "r")) == NULL) return 0; /* No interrupts file */ while ((fgets(line, 256, fp) != NULL) && (irq < max_nr_irqcpu)) { if (isdigit(line[2])) irq++; } fclose(fp); return (irq + NR_IRQPROC_PREALLOC);}/* *************************************************************************** * Allocate and init structures, according to system state *************************************************************************** */void sa_sys_init(unsigned int *flags){ /* How many processors on this machine ? */ if ((cpu_nr = get_cpu_nr(NR_CPUS)) > 0) SREALLOC(st_cpu, struct stats_one_cpu, STATS_ONE_CPU_SIZE * (cpu_nr + 1)); /* Get serial lines that support accounting */ if ((serial_nr = get_serial_lines())) { sadc_actflag |= A_SERIAL; SREALLOC(st_serial, struct stats_serial, STATS_SERIAL_SIZE * serial_nr); } /* Get number of interrupts available per processor */ if (cpu_nr > 0) { if ((irqcpu_nr = get_irqcpu_nb(NR_IRQS))) SREALLOC(st_irq_cpu, struct stats_irq_cpu, STATS_IRQ_CPU_SIZE * (cpu_nr + 1) * irqcpu_nr); } else /* IRQ per processor are not provided by sadc on UP machines */ irqcpu_nr = 0; /* Get number of network devices (interfaces) */ if ((iface_nr = get_net_dev())) { sadc_actflag |= A_NET_DEV + A_NET_EDEV; SREALLOC(st_net_dev, struct stats_net_dev, STATS_NET_DEV_SIZE * iface_nr); } /* * Get number of devices in /proc/{diskstats,partitions} * or number of disk_io entries in /proc/stat. * Alwyays done, since disk stats must be read at least for sar -b * if not for sar -d. */ if ((disk_nr = get_diskstats_dev_nr(CNT_DEV, CNT_USED_DEV)) > 0) { *flags |= S_F_HAS_DISKSTATS; disk_nr += NR_DISK_PREALLOC; SREALLOC(st_disk, struct disk_stats, DISK_STATS_SIZE * disk_nr); } else if ((disk_nr = get_ppartitions_dev_nr(CNT_DEV)) > 0) { *flags |= S_F_HAS_PPARTITIONS; disk_nr += NR_DISK_PREALLOC; SREALLOC(st_disk, struct disk_stats, DISK_STATS_SIZE * disk_nr); } else if ((disk_nr = get_disk_io_nr()) > 0) { disk_nr += NR_DISK_PREALLOC; SREALLOC(st_disk, struct disk_stats, DISK_STATS_SIZE * disk_nr); }}/* *************************************************************************** * If -L option used, request a non-blocking, exclusive lock on the file. * If lock would block, then another process (possibly sadc) has already * opened that file => exit. *************************************************************************** */int ask_for_flock(int fd, unsigned int *flags, int fatal){ /* Option -L may be used only if an outfile was specified on the command line */ if (USE_L_OPTION(*flags)) { /* * Yes: try to lock file. To make code portable, check for both EWOULDBLOCK * and EAGAIN return codes, and treat them the same (glibc documentation). * Indeed, some Linux ports (e.g. hppa-linux) do not equate EWOULDBLOCK and * EAGAIN like every other Linux port. */ if (flock(fd, LOCK_EX | LOCK_NB) < 0) { if ((((errno == EWOULDBLOCK) || (errno == EAGAIN)) && (fatal == FATAL)) || ((errno != EWOULDBLOCK) && (errno != EAGAIN))) { perror("flock"); exit(1); } /* Was unable to lock file: lock would have blocked... */ return 1; } else /* File successfully locked */ *flags |= S_F_FILE_LCK; } return 0;}/* *************************************************************************** * Fill system activity file header, then print it *************************************************************************** */void setup_file_hdr(int fd, size_t *file_stats_size){ int nb; struct tm loc_time; struct utsname header; /* First reset the structure */ memset(&file_hdr, 0, FILE_HDR_SIZE); /* Then get current date */ file_hdr.sa_ust_time = get_localtime(&loc_time); /* Ok, now fill the header */ file_hdr.sa_actflag = sadc_actflag; file_hdr.sa_magic = SA_MAGIC; file_hdr.sa_st_size = FILE_STATS_SIZE; file_hdr.sa_day = loc_time.tm_mday; file_hdr.sa_month = loc_time.tm_mon; file_hdr.sa_year = loc_time.tm_year; file_hdr.sa_sizeof_long = sizeof(long); file_hdr.sa_proc = cpu_nr; file_hdr.sa_nr_pid = pid_nr; file_hdr.sa_serial = serial_nr; file_hdr.sa_irqcpu = irqcpu_nr; file_hdr.sa_iface = iface_nr; if (GET_DISK(sadc_actflag)) file_hdr.sa_nr_disk = disk_nr; else file_hdr.sa_nr_disk = 0; *file_stats_size = FILE_STATS_SIZE; /* Get system name, release number and hostname */ uname(&header); strncpy(file_hdr.sa_sysname, header.sysname, UTSNAME_LEN); file_hdr.sa_sysname[UTSNAME_LEN - 1] = '\0'; strncpy(file_hdr.sa_nodename, header.nodename, UTSNAME_LEN); file_hdr.sa_nodename[UTSNAME_LEN - 1] = '\0'; strncpy(file_hdr.sa_release, header.release, UTSNAME_LEN); file_hdr.sa_release[UTSNAME_LEN - 1] = '\0'; /* Write file header */ if ((nb = write(fd, &file_hdr, FILE_HDR_SIZE)) != FILE_HDR_SIZE) { fprintf(stderr, _("Cannot write system activity file header: %s\n"), strerror(errno)); exit(2); }}/* *************************************************************************** * sadc called with interval and count parameters not set: * write a dummy record notifying a system restart. * This should typically be called this way at boot time, * before the cron daemon is started to avoid conflict with sa1/sa2 scripts. *************************************************************************** */void write_dummy_record(int ofd, size_t file_stats_size, unsigned int *flags){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -