/* *************************************************************************** * Display stats since system startup. * * IN: * @curr Index in array for current sample statistics. *************************************************************************** */ void write_stats_startup(int curr) { int i; /* Set to 0 previous structures corresponding to boot time */ memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE); record_hdr[!curr].record_type = R_STATS; record_hdr[!curr].hour = record_hdr[curr].hour; record_hdr[!curr].minute = record_hdr[curr].minute; record_hdr[!curr].second = record_hdr[curr].second; record_hdr[!curr].ust_time = record_hdr[curr].ust_time; for (i = 0; i < NR_ACT; i++) { if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { memset(act[i]->buf[!curr], 0, act[i]->msize * act[i]->nr * act[i]->nr2); } } flags |= S_F_SINCE_BOOT; dis = TRUE; write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET, ALL_ACTIVITIES); exit(0); }
/* *************************************************************************** * Determine if a stat header line has to be displayed. * * RETURNS: * TRUE if a header line has to be displayed. *************************************************************************** */ int check_line_hdr(void) { int i, rc = FALSE; /* Get number of options entered on the command line */ if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1) return TRUE; for (i = 0; i < NR_ACT; i++) { if (IS_SELECTED(act[i]->options)) { /* Special processing for activities using a bitmap */ if (act[i]->bitmap) { if (count_bits(act[i]->bitmap->b_array, BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) { rc = TRUE; } } else if (act[i]->nr > 1) { rc = TRUE; } /* Stop now since we have only one selected activity */ break; } } return rc; }
/* *************************************************************************** * Display the field list (used eg. in database format). * * IN: * @act_d Activity to display, or ~0 for all. *************************************************************************** */ void list_fields(unsigned int act_id) { int i, j; unsigned int msk; char *hl; char hline[HEADER_LINE_LEN] = ""; printf("# hostname;interval;timestamp"); for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { if (!HAS_MULTIPLE_OUTPUTS(act[i]->options)) { printf(";%s", act[i]->hdr_line); if ((act[i]->nr > 1) && DISPLAY_HORIZONTALLY(flags)) { printf("[...]"); } } else { msk = 1; strncpy(hline, act[i]->hdr_line, HEADER_LINE_LEN - 1); hline[HEADER_LINE_LEN - 1] = '\0'; for (hl = strtok(hline, "|"); hl; hl = strtok(NULL, "|"), msk <<= 1) { if ((hl != NULL) && ((act[i]->opt_flags & 0xff) & msk)) { if (strchr(hl, '&')) { j = strcspn(hl, "&"); if ((act[i]->opt_flags & 0xff00) & (msk << 8)) { /* Display whole header line */ *(hl + j) = ';'; printf(";%s", hl); } else { /* Display only the first part of the header line */ *(hl + j) = '\0'; printf(";%s", hl); } *(hl + j) = '&'; } else { printf(";%s", hl); } if ((act[i]->nr > 1) && DISPLAY_HORIZONTALLY(flags)) { printf("[...]"); } } } } } } printf("\n"); }
/* *************************************************************************** * Check that every selected activity actually belongs to the sequence list. * If not, then the activity should be unselected since it will not be sent * by sadc. An activity can be not sent if its number of items is null. * * IN: * @act_nr Size of sequence list. *************************************************************************** */ void reverse_check_act(unsigned int act_nr) { int i, j; for (i = 0; i < NR_ACT; i++) { if (IS_SELECTED(act[i]->options)) { for (j = 0; j < act_nr; j++) { if (id_seq[j] == act[i]->id) break; } if (j == act_nr) act[i]->options &= ~AO_SELECTED; } } }
/* *************************************************************************** * Print statistics average. * * IN: * @curr Index in array for current sample statistics. * @read_from_file Set to TRUE if stats are read from a system activity * data file. * @act_id Activity that can be displayed, or ~0 for all. * Remember that when reading stats from a file, only * one activity can be displayed at a time. *************************************************************************** */ void write_stats_avg(int curr, int read_from_file, unsigned int act_id) { int i; unsigned long long itv, g_itv; static __nr_t cpu_nr = -1; if (cpu_nr < 0) cpu_nr = act[get_activity_position(act, A_CPU)]->nr; /* Interval value in jiffies */ g_itv = get_interval(record_hdr[2].uptime, record_hdr[curr].uptime); if (cpu_nr > 1) itv = get_interval(record_hdr[2].uptime0, record_hdr[curr].uptime0); else itv = g_itv; strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN); timestamp[curr][TIMESTAMP_LEN - 1] = '\0'; strcpy(timestamp[!curr], timestamp[curr]); /* Test stdout */ TEST_STDOUT(STDOUT_FILENO); for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { /* Display current average activity statistics */ (*act[i]->f_print_avg)(act[i], 2, curr, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } if (read_from_file) { /* * Reset number of lines printed only if we read stats * from a system activity file. */ avg_count = 0; } }
/* *************************************************************************** * Print statistics average * * IN: * @curr Index in array for current sample statistics. * @read_from_file Set to TRUE if stats are read from a system activity * data file. * @act_id Activity that can be displayed, or ~0 for all. * Remember that when reading stats from a file, only * one activity can be displayed at a time. *************************************************************************** */ void write_stats_avg(int curr, int read_from_file, unsigned int act_id) { int i; unsigned long long itv, g_itv; static __nr_t cpu_nr = -1; if (cpu_nr < 0) cpu_nr = act[get_activity_position(act, A_CPU)]->nr; /* Interval value in jiffies */ g_itv = get_interval(record_hdr[2].uptime, record_hdr[curr].uptime); if (cpu_nr > 1) itv = get_interval(record_hdr[2].uptime0, record_hdr[curr].uptime0); else itv = g_itv; strcpy(timestamp[curr], _("Average:")); strcpy(timestamp[!curr], timestamp[curr]); /* Test stdout */ TEST_STDOUT(STDOUT_FILENO); for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { /* Display current average activity statistics */ if (NEEDS_GLOBAL_ITV(act[i]->options)) (*act[i]->f_print_avg)(act[i], 2, curr, g_itv); else (*act[i]->f_print_avg)(act[i], 2, curr, itv); } } if (read_from_file) { /* Reset counters only if we read stats from a system activity file */ memset(&asum, 0, STATS_SUM_SIZE); } }
/* *************************************************************************** * write_mech_stats() - * Replace the old write_stats_for_ppc() and write_stats_for_db(), * making it easier for them to remain in sync and print the same data. * * IN: * @curr Index in array for current sample statistics. * @dt Interval of time in seconds. * @itv Interval of time in jiffies. * @g_itv Interval of time in jiffies multiplied by the number of * processors. * @cur_date Date string for current record. * @cur_time Time string for current record. * @act_id Activity to display, or ~0 for all. *************************************************************************** */ void write_mech_stats(int curr, unsigned long dt, unsigned long long itv, unsigned long long g_itv, char *cur_date, char *cur_time, unsigned int act_id) { int i; char pre[80], temp[80]; /* Text at beginning of each line */ int isdb = (format == F_DB_OUTPUT); /* This substring appears on every output line, preformat it here */ snprintf(pre, 80, "%s%s%ld%s", file_hdr.sa_nodename, seps[isdb], dt, seps[isdb]); if (strlen(cur_date)) { snprintf(temp, 80, "%s%s ", pre, cur_date); } else { strcpy(temp, pre); } snprintf(pre, 80, "%s%s%s", temp, cur_time, strlen(cur_date) && !PRINT_LOCAL_TIME(flags) && !PRINT_TRUE_TIME(flags) ? " UTC" : ""); pre[79] = '\0'; if (DISPLAY_HORIZONTALLY(flags)) { printf("%s", pre); } for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { (*act[i]->f_render)(act[i], isdb, pre, curr, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } if (DISPLAY_HORIZONTALLY(flags)) { printf("\n"); } }
/* *************************************************************************** * Display the field list (used eg. in database format). * * IN: * @act_d Activity to display, or ~0 for all. *************************************************************************** */ void list_fields(unsigned int act_id) { int i; unsigned int msk; char *hl; char hline[HEADER_LINE_LEN]; printf("# hostname;interval;timestamp"); for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { if (!HAS_MULTIPLE_OUTPUTS(act[i]->options)) { printf(";%s", act[i]->hdr_line); if ((act[i]->nr > 1) && DISPLAY_HORIZONTALLY(flags)) { printf("[...]"); } } else { msk = 1; strcpy(hline, act[i]->hdr_line); for (hl = strtok(hline, "|"); hl; hl = strtok(NULL, "|"), msk <<= 1) { if ((hl != NULL) && (act[i]->opt_flags & msk)) { printf(";%s", hl); if ((act[i]->nr > 1) && DISPLAY_HORIZONTALLY(flags)) { printf("[...]"); } } } } } } printf("\n"); }
/* *************************************************************************** * Main entry to the sar program. *************************************************************************** */ int main(int argc, char **argv) { int i, rc, opt = 1, args_idx = 2; int fd[2]; int day_offset = 0; char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN]; char ltemp[20]; /* Get HZ */ get_HZ(); /* Compute page shift in kB */ get_kb_shift(); from_file[0] = to_file[0] = '\0'; #ifdef USE_NLS /* Init National Language Support */ init_nls(); #endif tm_start.use = tm_end.use = FALSE; /* Allocate and init activity bitmaps */ allocate_bitmaps(act); init_structures(); /* Process options */ while (opt < argc) { if (!strcmp(argv[opt], "-I")) { if (argv[++opt]) { /* Parse -I option */ if (parse_sar_I_opt(argv, &opt, act)) { usage(argv[0]); } } else { usage(argv[0]); } } else if (!strcmp(argv[opt], "-D")) { /* Option to tell sar to write to saYYYYMMDD data files */ flags |= S_F_SA_YYYYMMDD; opt++; } else if (!strcmp(argv[opt], "-P")) { /* Parse -P option */ if (parse_sa_P_opt(argv, &opt, &flags, act)) { usage(argv[0]); } } else if (!strcmp(argv[opt], "-o")) { if (to_file[0]) { /* Output file already specified */ usage(argv[0]); } /* Save stats to a file */ if ((argv[++opt]) && strncmp(argv[opt], "-", 1) && (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) { strncpy(to_file, argv[opt++], MAX_FILE_LEN); to_file[MAX_FILE_LEN - 1] = '\0'; } else { strcpy(to_file, "-"); } } else if (!strcmp(argv[opt], "-f")) { if (from_file[0]) { /* Input file already specified */ usage(argv[0]); } /* Read stats from a file */ if ((argv[++opt]) && strncmp(argv[opt], "-", 1) && (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) { strncpy(from_file, argv[opt++], MAX_FILE_LEN); from_file[MAX_FILE_LEN - 1] = '\0'; /* Check if this is an alternate directory for sa files */ check_alt_sa_dir(from_file, day_offset, -1); } else { set_default_file(from_file, day_offset, -1); } } else if (!strcmp(argv[opt], "-s")) { /* Get time start */ if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) { usage(argv[0]); } } else if (!strcmp(argv[opt], "-e")) { /* Get time end */ if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) { usage(argv[0]); } } else if (!strcmp(argv[opt], "-h")) { /* Display help message */ display_help(argv[0]); } else if (!strcmp(argv[opt], "-i")) { if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) { usage(argv[0]); } interval = atol(argv[opt++]); if (interval < 1) { usage(argv[0]); } flags |= S_F_INTERVAL_SET; } else if (!strcmp(argv[opt], "-m")) { if (argv[++opt]) { /* Parse option -m */ if (parse_sar_m_opt(argv, &opt, act)) { usage(argv[0]); } } else { usage(argv[0]); } } else if (!strcmp(argv[opt], "-n")) { if (argv[++opt]) { /* Parse option -n */ if (parse_sar_n_opt(argv, &opt, act)) { usage(argv[0]); } } else { usage(argv[0]); } } else if ((strlen(argv[opt]) > 1) && (strlen(argv[opt]) < 4) && !strncmp(argv[opt], "-", 1) && (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) { day_offset = atoi(argv[opt++] + 1); } else if (!strncmp(argv[opt], "-", 1)) { /* Other options not previously tested */ if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) { if (rc == 1) { usage(argv[0]); } exit(1); } opt++; } else if (interval < 0) { /* Get interval */ if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) { usage(argv[0]); } interval = atol(argv[opt++]); if (interval < 0) { usage(argv[0]); } } else { /* Get count value */ if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) || !interval) { usage(argv[0]); } if (count) { /* Count parameter already set */ usage(argv[0]); } count = atol(argv[opt++]); if (count < 1) { usage(argv[0]); } } } /* 'sar' is equivalent to 'sar -f' */ if ((argc == 1) || ((interval < 0) && !from_file[0] && !to_file[0])) { set_default_file(from_file, day_offset, -1); } if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) { tm_end.tm_hour += 24; } /* * Check option dependencies. */ /* You read from a file OR you write to it... */ if (from_file[0] && to_file[0]) { fprintf(stderr, _("-f and -o options are mutually exclusive\n")); exit(1); } /* Use time start or option -i only when reading stats from a file */ if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) { fprintf(stderr, _("Not reading from a system activity file (use -f option)\n")); exit(1); } /* Don't print stats since boot time if -o or -f options are used */ if (!interval && (from_file[0] || to_file[0])) { usage(argv[0]); } /* Cannot enter a day shift with -o option */ if (to_file[0] && day_offset) { usage(argv[0]); } if (USE_PRETTY_OPTION(flags)) { dm_major = get_devmap_major(); } if (!count) { /* * count parameter not set: Display all the contents of the file * or generate a report continuously. */ count = -1; } /* Default is CPU activity... */ select_default_activity(act); /* Reading stats from file: */ if (from_file[0]) { if (interval < 0) { interval = 1; } /* Read stats from file */ read_stats_from_file(from_file); /* Free stuctures and activity bitmaps */ free_bitmaps(act); free_structures(act); return 0; } /* Reading stats from sadc: */ /* Create anonymous pipe */ if (pipe(fd) == -1) { perror("pipe"); exit(4); } switch (fork()) { case -1: perror("fork"); exit(4); break; case 0: /* Child */ if (dup2(fd[1], STDOUT_FILENO) < 0) { perror("dup2"); exit(4); } CLOSE_ALL(fd); /* * Prepare options for sadc. */ /* Program name */ salloc(0, SADC); /* Interval value */ if (interval < 0) { usage(argv[0]); } else if (!interval) { strcpy(ltemp, "1"); } else { sprintf(ltemp, "%ld", interval); } salloc(1, ltemp); /* Count number */ if (count >= 0) { sprintf(ltemp, "%ld", count + 1); salloc(args_idx++, ltemp); } /* Flags to be passed to sadc */ salloc(args_idx++, "-z"); /* Writing data to a file (option -o) */ if (to_file[0]) { /* Set option -D if entered */ if (USE_SA_YYYYMMDD(flags)) { salloc(args_idx++, "-D"); } /* Collect all possible activities (option -S XALL for sadc) */ salloc(args_idx++, "-S"); salloc(args_idx++, K_XALL); /* Outfile arg */ salloc(args_idx++, to_file); } else { /* * If option -o hasn't been used, then tell sadc * to collect only activities that will be displayed. */ int act_id = 0; for (i = 0; i < NR_ACT; i++) { if (IS_SELECTED(act[i]->options)) { act_id |= act[i]->group; } } if (act_id) { act_id <<= 8; snprintf(ltemp, 19, "%d", act_id); ltemp[19] = '\0'; salloc(args_idx++, "-S"); salloc(args_idx++, ltemp); } } /* Last arg is NULL */ args[args_idx] = NULL; /* Call now the data collector */ execv(SADC_PATH, args); execvp(SADC, args); /* * Note: Don't use execl/execlp since we don't have a fixed number of * args to give to sadc. */ fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC); perror("exec"); exit(4); break; default: /* Parent */ if (dup2(fd[0], STDIN_FILENO) < 0) { perror("dup2"); exit(4); } CLOSE_ALL(fd); /* Get now the statistics */ read_stats(); break; } /* Free structures and activity bitmaps */ free_bitmaps(act); free_structures(act); return 0; }
/* *************************************************************************** * Print system statistics. * * IN: * @curr Index in array for current sample statistics. * @read_from_file Set to TRUE if stats are read from a system activity * data file. * @use_tm_start Set to TRUE if option -s has been used. * @use_tm_end Set to TRUE if option -e has been used. * @reset Set to TRUE if last_uptime variable should be * reinitialized (used in next_slice() function). * @act_id Activity that can be displayed or ~0 for all. * Remember that when reading stats from a file, only * one activity can be displayed at a time. * @reset_cd TRUE if static cross_day variable should be reset * (see below). * * OUT: * @cnt Number of remaining lines to display. * * RETURNS: * 1 if stats have been successfully displayed, and 0 otherwise. *************************************************************************** */ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start, int use_tm_end, int reset, unsigned int act_id, int reset_cd) { int i; unsigned long long itv, g_itv; static int cross_day = 0; static __nr_t cpu_nr = -1; if (cpu_nr < 0) cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr; if (reset_cd) { /* * cross_day is a static variable that is set to 1 when the first * record of stats from a new day is read from a unique data file * (in the case where the file contains data from two consecutive * days). When set to 1, every following records timestamp will * have its hour value increased by 24. * Yet when a new activity (being read from the file) is going to * be displayed, we start reading the file from the beginning * again, and so cross_day should be reset in this case. */ cross_day = 0; } /* Check time (1) */ if (read_from_file) { if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0, reset, interval)) /* Not close enough to desired interval */ return 0; } /* Set previous timestamp */ if (set_record_timestamp_string(!curr, timestamp[!curr], 16)) return 0; /* Set current timestamp */ if (set_record_timestamp_string(curr, timestamp[curr], 16)) return 0; /* Check if we are beginning a new day */ if (use_tm_start && record_hdr[!curr].ust_time && (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) && (record_hdr[curr].hour < record_hdr[!curr].hour)) { cross_day = 1; } if (cross_day) { /* * This is necessary if we want to properly handle something like: * sar -s time_start -e time_end with * time_start(day D) > time_end(day D+1) */ rectime.tm_hour +=24; } /* Check time (2) */ if (use_tm_start && (datecmp(&rectime, &tm_start) < 0)) /* it's too soon... */ return 0; /* Get interval values */ get_itv_value(&record_hdr[curr], &record_hdr[!curr], cpu_nr, &itv, &g_itv); /* Check time (3) */ if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) { /* It's too late... */ *cnt = 0; return 0; } avg_count++; /* Test stdout */ TEST_STDOUT(STDOUT_FILENO); for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { /* Display current activity statistics */ (*act[i]->f_print)(act[i], !curr, curr, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } return 1; }
/* *************************************************************************** * Display file contents in selected format (logic #2). * Logic #2: Grouped by activity. Sorted by timestamp. Stop on RESTART * records. * Formats: ppc, CSV * * IN: * @ifd File descriptor of input file. * @file_actlst List of (known or unknown) activities in file. * @cpu_nr Number of processors for current activity data file. * @rectime Structure where timestamp (expressed in local time or in UTC * depending on whether options -T/-t have been used or not) can * be saved for current record. * @loctime Structure where timestamp (expressed in local time) can be * saved for current record. * @file Name of file being read. * @file_magic file_magic structure filled with file magic header data. *************************************************************************** */ void logic2_display_loop(int ifd, struct file_activity *file_actlst, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime, char *file, struct file_magic *file_magic) { int i, p; int curr = 1, rtype; int eosaf = TRUE, reset = FALSE; long cnt = 1; off_t fpos; /* Read system statistics from file */ do { /* * If this record is a special (RESTART or COMMENT) one, print it and * (try to) get another one. */ do { if (read_next_sample(ifd, IGNORE_NOTHING, 0, file, &rtype, 0, file_magic, file_actlst, rectime, loctime)) /* End of sa data file */ return; } while ((rtype == R_RESTART) || (rtype == R_COMMENT) || (tm_start.use && (datecmp(loctime, &tm_start) < 0)) || (tm_end.use && (datecmp(loctime, &tm_end) >= 0))); /* Save the first stats collected. Used for example in next_slice() function */ copy_structures(act, id_seq, record_hdr, 2, 0); /* Set flag to reset last_uptime variable. Should be done after a LINUX RESTART record */ reset = TRUE; /* Save current file position */ if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) { perror("lseek"); exit(2); } /* Read and write stats located between two possible Linux restarts */ if (DISPLAY_HORIZONTALLY(flags)) { /* * If stats are displayed horizontally, then all activities * are printed on the same line. */ rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, ALL_ACTIVITIES, &reset, file_actlst, cpu_nr, rectime, loctime, file, file_magic); } else { /* For each requested activity... */ for (i = 0; i < NR_ACT; i++) { if (!id_seq[i]) continue; p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND); if (!IS_SELECTED(act[p]->options)) continue; if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) { rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, act[p]->id, &reset, file_actlst, cpu_nr, rectime, loctime, file, file_magic); } else { unsigned int optf, msk; optf = act[p]->opt_flags; for (msk = 1; msk < 0x100; msk <<= 1) { if ((act[p]->opt_flags & 0xff) & msk) { act[p]->opt_flags &= (0xffffff00 + msk); rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, act[p]->id, &reset, file_actlst, cpu_nr, rectime, loctime, file, file_magic); act[p]->opt_flags = optf; } } } } } if (!cnt) { /* Go to next Linux restart, if possible */ do { eosaf = read_next_sample(ifd, IGNORE_RESTART | DONT_READ_VOLATILE, curr, file, &rtype, 0, file_magic, file_actlst, rectime, loctime); } while (!eosaf && (rtype != R_RESTART)); } /* * The last record we read was a RESTART one: Print it. * NB: Unlike COMMENTS records (which are displayed for each * activity), RESTART ones are only displayed once. */ if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) { print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end, R_RESTART, ifd, rectime, loctime, file, 0, file_magic, &file_hdr, act, fmt[f_position]); } } while (!eosaf); }
/* *************************************************************************** * Display activities for non textual formats. * * IN: * @ifd File descriptor of input file. * @file_actlst List of (known or unknown) activities in file. * @cpu_nr Number of processors for current activity data file. * @rectime Structure where timestamp (expressed in local time or in UTC * depending on whether option -t has been used or not) can be * saved for current record. * @loctime Structure where timestamp (expressed in local time) can be * saved for current record. *************************************************************************** */ void main_display_loop(int ifd, struct file_activity *file_actlst, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime) { int i, p; int curr = 1, rtype; int eosaf = TRUE, reset = FALSE; long cnt = 1; off_t fpos; /* Read system statistics from file */ do { /* * If this record is a special (RESTART or COMMENT) one, print it and * (try to) get another one. */ do { if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE)) /* End of sa data file */ return; rtype = record_hdr[0].record_type; if ((rtype == R_RESTART) || (rtype == R_COMMENT)) { sadf_print_special(0, tm_start.use, tm_end.use, rtype, ifd, rectime, loctime); } else { /* * OK: Previous record was not a special one. * So read now the extra fields. */ read_file_stat_bunch(act, 0, ifd, file_hdr.sa_nr_act, file_actlst); sadf_get_record_timestamp_struct(0, rectime, loctime); } } while ((rtype == R_RESTART) || (rtype == R_COMMENT) || (tm_start.use && (datecmp(loctime, &tm_start) < 0)) || (tm_end.use && (datecmp(loctime, &tm_end) >= 0))); /* Save the first stats collected. Will be used to compute the average */ copy_structures(act, id_seq, record_hdr, 2, 0); /* Set flag to reset last_uptime variable. Should be done after a LINUX RESTART record */ reset = TRUE; /* Save current file position */ if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) { perror("lseek"); exit(2); } /* Read and write stats located between two possible Linux restarts */ if (DISPLAY_HORIZONTALLY(flags)) { /* * If stats are displayed horizontally, then all activities * are printed on the same line. */ rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, ALL_ACTIVITIES, &reset, file_actlst, cpu_nr, rectime, loctime); } else { /* For each requested activity... */ for (i = 0; i < NR_ACT; i++) { if (!id_seq[i]) continue; if ((p = get_activity_position(act, id_seq[i])) < 0) { /* Should never happen */ PANIC(1); } if (!IS_SELECTED(act[p]->options)) continue; if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) { rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, act[p]->id, &reset, file_actlst, cpu_nr, rectime, loctime); } else { unsigned int optf, msk; optf = act[p]->opt_flags; for (msk = 1; msk < 0x10; msk <<= 1) { if (act[p]->opt_flags & msk) { act[p]->opt_flags &= msk; rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, act[p]->id, &reset, file_actlst, cpu_nr, rectime, loctime); act[p]->opt_flags = optf; } } } } } if (!cnt) { /* Go to next Linux restart, if possible */ do { eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE, SOFT_SIZE); rtype = record_hdr[curr].record_type; if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) { read_file_stat_bunch(act, curr, ifd, file_hdr.sa_nr_act, file_actlst); } else if (!eosaf && (rtype == R_COMMENT)) { /* This was a COMMENT record: print it */ sadf_print_special(curr, tm_start.use, tm_end.use, R_COMMENT, ifd, rectime, loctime); } } while (!eosaf && (rtype != R_RESTART)); } /* The last record we read was a RESTART one: Print it */ if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) { sadf_print_special(curr, tm_start.use, tm_end.use, R_RESTART, ifd, rectime, loctime); } } while (!eosaf); }
/* *************************************************************************** * Display *one* sample of statistics for one or several activities, * checking that all conditions are met before printing (time start, time * end, interval). Current record should be a record of statistics (R_STATS), * not a special one (R_RESTART or R_COMMENT). * * IN: * @curr Index in array for current sample statistics. * @use_tm_start Set to TRUE if option -s has been used. * @use_tm_end Set to TRUE if option -e has been used. * @reset Set to TRUE if last_uptime should be reinitialized * (used in next_slice() function). * @parm Pointer on parameters depending on output format * (eg.: number of tabulations to print). * @cpu_nr Number of processors. * @rectime Structure where timestamp (expressed in local time * or in UTC depending on whether options -T/-t have * been used or not) has been saved for current record. * @loctime Structure where timestamp (expressed in local time) * has been saved for current record. * @reset_cd TRUE if static cross_day variable should be reset. * @act_id Activity to display (only for formats where * activities are displayed one at a time) or * ALL_ACTIVITIES for all. * * OUT: * @cnt Set to 0 to indicate that no other lines of stats * should be displayed. * * RETURNS: * 1 if stats have been successfully displayed. *************************************************************************** */ int generic_write_stats(int curr, int use_tm_start, int use_tm_end, int reset, long *cnt, void *parm, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime, int reset_cd, unsigned int act_id) { int i; unsigned long long dt, itv, g_itv; char cur_date[32], cur_time[32], *pre = NULL; static int cross_day = FALSE; if (reset_cd) { /* * See note in sar.c. * NB: Reseting cross_day is needed only if datafile * may be rewinded (eg. in db or ppc output formats). */ cross_day = 0; } /* * Check time (1). * For this first check, we use the time interval entered on * the command line. This is equivalent to sar's option -i which * selects records at seconds as close as possible to the number * specified by the interval parameter. */ if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0, reset, interval)) /* Not close enough to desired interval */ return 0; /* Check if we are beginning a new day */ if (use_tm_start && record_hdr[!curr].ust_time && (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) && (record_hdr[curr].hour < record_hdr[!curr].hour)) { cross_day = TRUE; } if (cross_day) { /* * This is necessary if we want to properly handle something like: * sar -s time_start -e time_end with * time_start(day D) > time_end(day D+1) */ loctime->tm_hour += 24; } /* Check time (2) */ if (use_tm_start && (datecmp(loctime, &tm_start) < 0)) /* it's too soon... */ return 0; /* Get interval values */ get_itv_value(&record_hdr[curr], &record_hdr[!curr], cpu_nr, &itv, &g_itv); /* Check time (3) */ if (use_tm_end && (datecmp(loctime, &tm_end) > 0)) { /* It's too late... */ *cnt = 0; return 0; } dt = itv / HZ; /* Correct rounding error for dt */ if ((itv % HZ) >= (HZ / 2)) { dt++; } /* Set date and time strings for current record */ set_record_timestamp_string(flags, &record_hdr[curr], cur_date, cur_time, 32, rectime); if (*fmt[f_position]->f_timestamp) { pre = (char *) (*fmt[f_position]->f_timestamp)(parm, F_BEGIN, cur_date, cur_time, dt, &file_hdr, flags); } /* Display statistics */ for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if ((TEST_MARKUP(fmt[f_position]->options) && CLOSE_MARKUP(act[i]->options)) || (IS_SELECTED(act[i]->options) && (act[i]->nr > 0))) { if (format == F_JSON_OUTPUT) { /* JSON output */ int *tab = (int *) parm; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { if (*fmt[f_position]->f_timestamp) { (*fmt[f_position]->f_timestamp)(tab, F_MAIN, cur_date, cur_time, dt, &file_hdr, flags); } } (*act[i]->f_json_print)(act[i], curr, *tab, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } else if (format == F_XML_OUTPUT) { /* XML output */ int *tab = (int *) parm; (*act[i]->f_xml_print)(act[i], curr, *tab, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } else if (format == F_SVG_OUTPUT) { /* SVG output */ struct svg_parm *svg_p = (struct svg_parm *) parm; svg_p->dt = (unsigned long) dt; (*act[i]->f_svg_print)(act[i], curr, F_MAIN, svg_p, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv, &record_hdr[curr]); } else { /* Other output formats: db, ppc */ (*act[i]->f_render)(act[i], (format == F_DB_OUTPUT), pre, curr, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } } if (*fmt[f_position]->f_timestamp) { (*fmt[f_position]->f_timestamp)(parm, F_END, cur_date, cur_time, dt, &file_hdr, flags); } return 1; }
/* *************************************************************************** * Compute the number of SVG graphs to display. Each activity selected may * have several graphs. Moreover we have to take into account volatile * activities (eg. CPU) for which the number of graphs will depend on the * highest number of items (eg. maximum number of CPU) saved in the file. * This number may be higher than the real number of graphs that will be * displayed since some items have a preallocation constant. * * IN: * @ifd File descriptor of input file. * @file Name of file being read. * @file_magic file_magic structure filled with file magic header data. * @file_actlst List of (known or unknown) activities in file. * @rectime Structure where timestamp (expressed in local time or in UTC * depending on whether options -T/-t have been used or not) can * be saved for current record. * @loctime Structure where timestamp (expressed in local time) can be * saved for current record. * * RETURNS: * Total number of graphs to display, taking into account only activities * to be displayed, and selected period of time (options -s/-e). *************************************************************************** */ int get_svg_graph_nr(int ifd, char *file, struct file_magic *file_magic, struct file_activity *file_actlst, struct tm *rectime, struct tm *loctime) { int i, n, p, eosaf; int rtype, new_tot_g_nr, tot_g_nr = 0; off_t fpos; __nr_t save_act_nr[NR_ACT] = {0}; /* Save current file position and items number */ if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) { perror("lseek"); exit(2); } sr_act_nr(save_act_nr, DO_SAVE); /* Init total number of graphs for each activity */ for (i = 0; i < NR_ACT; i++) { id_g_nr[i] = 0; } /* Look for the first record that will be displayed */ do { eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS, 0, file, &rtype, 0, file_magic, file_actlst, rectime, loctime); if (eosaf) /* No record to display => no graph too */ return 0; } while ((tm_start.use && (datecmp(loctime, &tm_start) < 0)) || (tm_end.use && (datecmp(loctime, &tm_end) >= 0))); do { new_tot_g_nr = 0; for (i = 0; i < NR_ACT; i++) { if (!id_seq[i]) continue; p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND); if (!IS_SELECTED(act[p]->options)) continue; if (ONE_GRAPH_PER_ITEM(act[p]->options)) { n = act[p]->g_nr * act[p]->nr; } else { n = act[p]->g_nr; } if (n > id_g_nr[i]) { id_g_nr[i] = n; } new_tot_g_nr += n; } if (new_tot_g_nr > tot_g_nr) { tot_g_nr = new_tot_g_nr; } do { eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS, 0, file, &rtype, 0, file_magic, file_actlst, rectime, loctime); if (eosaf || (tm_end.use && (datecmp(loctime, &tm_end) >= 0))) /* End of data file or end time exceeded */ break; } while (rtype != R_RESTART); if (eosaf || (tm_end.use && (datecmp(loctime, &tm_end) >= 0))) /* * End of file, or end time exceeded: * Current number of graphs is up-to-date. */ break; /* * If we have found a RESTART record then we have also read the list of volatile * activities following it, reallocated the structures and changed the number of * items (act[p]->nr) for those volatile activities. So loop again to compute * the new total number of graphs. */ } while (rtype == R_RESTART); /* Rewind file and restore items number */ if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } sr_act_nr(save_act_nr, DO_RESTORE); return tot_g_nr; }
/* *************************************************************************** * Display file contents in selected format (logic #3). * Logic #3: Special logic for SVG output format. * Formats: SVG * * IN: * @ifd File descriptor of input file. * @file_actlst List of (known or unknown) activities in file. * @cpu_nr Number of processors for current activity data file. * @rectime Structure where timestamp (expressed in local time or in UTC * depending on whether options -T/-t have been used or not) can * be saved for current record. * @loctime Structure where timestamp (expressed in local time) can be * saved for current record. * @file Name of file being read. * @file_magic file_magic structure filled with file magic header data. *************************************************************************** */ void logic3_display_loop(int ifd, struct file_activity *file_actlst, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime, char *file, struct file_magic *file_magic) { int i, p; int curr = 1, rtype, g_nr = 0; int eosaf = TRUE, reset = TRUE; long cnt = 1; off_t fpos; int graph_nr = 0; __nr_t save_act_nr[NR_ACT] = {0}; /* Use a decimal point to make SVG code locale independent */ setlocale(LC_NUMERIC, "C"); /* Calculate the number of graphs to display */ graph_nr = get_svg_graph_nr(ifd, file, file_magic, file_actlst, rectime, loctime); if (!graph_nr) /* No graph to display */ return; /* Print SVG header */ if (*fmt[f_position]->f_header) { (*fmt[f_position]->f_header)(&graph_nr, F_BEGIN + F_MAIN, file, file_magic, &file_hdr, cpu_nr, act, id_seq); } /* * If this record is a special (RESTART or COMMENT) one, ignore it and * (try to) get another one. */ do { if (read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT, 0, file, &rtype, 0, file_magic, file_actlst, rectime, loctime)) /* End of sa data file */ return; } while ((rtype == R_RESTART) || (rtype == R_COMMENT) || (tm_start.use && (datecmp(loctime, &tm_start) < 0)) || (tm_end.use && (datecmp(loctime, &tm_end) >= 0))); /* Save the first stats collected. Used for example in next_slice() function */ copy_structures(act, id_seq, record_hdr, 2, 0); /* Save current file position */ if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) { perror("lseek"); exit(2); } /* Save number of activities items for current file position */ sr_act_nr(save_act_nr, DO_SAVE); /* For each requested activity, display graphs */ for (i = 0; i < NR_ACT; i++) { if (!id_seq[i]) continue; p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND); if (!IS_SELECTED(act[p]->options) || !act[p]->g_nr) continue; if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) { display_curr_act_graphs(ifd, fpos, &curr, &cnt, &eosaf, p, &reset, file_actlst, cpu_nr, rectime, loctime, file, file_magic, save_act_nr, &g_nr); } else { unsigned int optf, msk; optf = act[p]->opt_flags; for (msk = 1; msk < 0x100; msk <<= 1) { if ((act[p]->opt_flags & 0xff) & msk) { act[p]->opt_flags &= (0xffffff00 + msk); display_curr_act_graphs(ifd, fpos, &curr, &cnt, &eosaf, p, &reset, file_actlst, cpu_nr, rectime, loctime, file, file_magic, save_act_nr, &g_nr); act[p]->opt_flags = optf; } } } } /* Print SVG trailer */ if (*fmt[f_position]->f_header) { (*fmt[f_position]->f_header)(&graph_nr, F_END, file, file_magic, &file_hdr, cpu_nr, act, id_seq); } }
/* *************************************************************************** * Print system statistics. * * IN: * @curr Index in array for current sample statistics. * @read_from_file Set to TRUE if stats are read from a system activity * data file. * @use_tm_start Set to TRUE if option -s has been used. * @use_tm_end Set to TRUE if option -e has been used. * @reset Set to TRUE if last_uptime variable should be * reinitialized (used in next_slice() function). * @act_id Activity that can be displayed or ~0 for all. * Remember that when reading stats from a file, only * one activity can be displayed at a time. * * OUT: * @cnt Number of remaining lines to display. * * RETURNS: * 1 if stats have been successfully displayed, and 0 otherwise. *************************************************************************** */ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start, int use_tm_end, int reset, unsigned int act_id) { int i; unsigned long long itv, g_itv; static int cross_day = 0; static __nr_t cpu_nr = -1; if (cpu_nr < 0) cpu_nr = act[get_activity_position(act, A_CPU)]->nr; /* Check time (1) */ if (read_from_file) { if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0, reset, interval)) /* Not close enough to desired interval */ return 0; } /* Set previous timestamp */ if (set_record_timestamp_string(!curr, timestamp[!curr], 16)) return 0; /* Set current timestamp */ if (set_record_timestamp_string(curr, timestamp[curr], 16)) return 0; /* Check if we are beginning a new day */ if (use_tm_start && record_hdr[!curr].ust_time && (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) && (record_hdr[curr].hour < record_hdr[!curr].hour)) { cross_day = 1; } if (cross_day) { /* * This is necessary if we want to properly handle something like: * sar -s time_start -e time_end with * time_start(day D) > time_end(day D+1) */ rectime.tm_hour +=24; } /* Check time (2) */ if (use_tm_start && (datecmp(&rectime, &tm_start) < 0)) /* it's too soon... */ return 0; /* Get interval values */ get_itv_value(&record_hdr[curr], &record_hdr[!curr], cpu_nr, &itv, &g_itv); /* Check time (3) */ if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) { /* It's too late... */ *cnt = 0; return 0; } avg_count++; /* Test stdout */ TEST_STDOUT(STDOUT_FILENO); for (i = 0; i < NR_ACT; i++) { if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id)) continue; if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { /* Display current activity statistics */ (*act[i]->f_print)(act[i], !curr, curr, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } return 1; }
/* *************************************************************************** * Display activity records for textual (XML-like) formats. * * IN: * @curr Index in array for current sample statistics. * @use_tm_start Set to TRUE if option -s has been used. * @use_tm_end Set to TRUE if option -e has been used. * @reset Set to TRUE if last_uptime should be reinitialized * (used in next_slice() function). * @tab Number of tabulations to print. * @cpu_nr Number of processors. * @rectime Structure where timestamp (expressed in local time * or in UTC depending on whether option -t has been * used or not) can be saved for current record. * @loctime Structure where timestamp (expressed in local time) * can be saved for current record. * * OUT: * @cnt Set to 0 to indicate that no other lines of stats * should be displayed. * * RETURNS: * 1 if stats have been successfully displayed. *************************************************************************** */ int write_textual_stats(int curr, int use_tm_start, int use_tm_end, int reset, long *cnt, int tab, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime) { int i; unsigned long long dt, itv, g_itv; char cur_date[32], cur_time[32]; static int cross_day = FALSE; /* Fill timestamp structure (rectime) for current record */ sadf_get_record_timestamp_struct(curr, rectime, loctime); /* * Check time (1). * For this first check, we use the time interval entered on * the command line. This is equivalent to sar's option -i which * selects records at seconds as close as possible to the number * specified by the interval parameter. */ if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0, reset, interval)) /* Not close enough to desired interval */ return 0; /* Check if we are beginning a new day */ if (use_tm_start && record_hdr[!curr].ust_time && (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) && (record_hdr[curr].hour < record_hdr[!curr].hour)) { cross_day = TRUE; } if (cross_day) { /* * This is necessary if we want to properly handle something like: * sar -s time_start -e time_end with * time_start(day D) > time_end(day D+1) */ loctime->tm_hour += 24; } /* Check time (2) */ if (use_tm_start && (datecmp(loctime, &tm_start) < 0)) /* it's too soon... */ return 0; /* Get interval values */ get_itv_value(&record_hdr[curr], &record_hdr[!curr], cpu_nr, &itv, &g_itv); /* Check time (3) */ if (use_tm_end && (datecmp(loctime, &tm_end) > 0)) { /* It's too late... */ *cnt = 0; return 0; } dt = itv / HZ; /* Correct rounding error for dt */ if ((itv % HZ) >= (HZ / 2)) { dt++; } /* Set date and time strings for current record */ set_record_timestamp_string(curr, cur_date, cur_time, 32, rectime); if (*fmt[f_position]->f_timestamp) { (*fmt[f_position]->f_timestamp)(&tab, F_BEGIN, cur_date, cur_time, !PRINT_TRUE_TIME(flags), dt); } if (format == F_XML_OUTPUT) { tab++; } /* Display textual statistics */ for (i = 0; i < NR_ACT; i++) { /* This code is not generic at all...! */ if (format == F_JSON_OUTPUT) { /* JSON output */ if (CLOSE_MARKUP(act[i]->options) || (IS_SELECTED(act[i]->options) && (act[i]->nr > 0))) { if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) { printf(","); if (*fmt[f_position]->f_timestamp) { (*fmt[f_position]->f_timestamp)(&tab, F_MAIN, cur_date, cur_time, !PRINT_TRUE_TIME(flags), dt); } } (*act[i]->f_json_print)(act[i], curr, tab, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } else { /* XML output */ if (CLOSE_MARKUP(act[i]->options) || (IS_SELECTED(act[i]->options) && (act[i]->nr > 0))) { (*act[i]->f_xml_print)(act[i], curr, tab, NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv); } } } if (*fmt[f_position]->f_timestamp) { (*fmt[f_position]->f_timestamp)(&tab, F_END, cur_date, cur_time, !PRINT_TRUE_TIME(flags), dt); } return 1; }
/* *************************************************************************** * Read statistics from a system activity data file. * * IN: * @from_file Input file name. *************************************************************************** */ void read_stats_from_file(char from_file[]) { struct file_magic file_magic; struct file_activity *file_actlst = NULL; int curr = 1, i, p; int ifd, rtype; int rows, eosaf = TRUE, reset = FALSE; long cnt = 1; off_t fpos; /* Get window size */ rows = get_win_height(); /* Read file headers and activity list */ check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr, &file_actlst, id_seq, FALSE); /* Perform required allocations */ allocate_structures(act); /* Print report header */ print_report_hdr(flags, &rectime, &file_hdr, act[get_activity_position(act, A_CPU)]->nr); /* Read system statistics from file */ do { /* * If this record is a special (RESTART or COMMENT) one, print it and * (try to) get another one. */ do { if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE)) /* End of sa data file */ return; rtype = record_hdr[0].record_type; if ((rtype == R_RESTART) || (rtype == R_COMMENT)) { sar_print_special(0, tm_start.use, tm_end.use, rtype, ifd, from_file, &file_magic); } else { /* * OK: Previous record was not a special one. * So read now the extra fields. */ read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr, file_actlst); if (sar_get_record_timestamp_struct(0)) /* * An error was detected. * The timestamp hasn't been updated. */ continue; } } while ((rtype == R_RESTART) || (rtype == R_COMMENT) || (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) || (tm_end.use && (datecmp(&rectime, &tm_end) >=0))); /* Save the first stats collected. Will be used to compute the average */ copy_structures(act, id_seq, record_hdr, 2, 0); reset = TRUE; /* Set flag to reset last_uptime variable */ /* Save current file position */ if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) { perror("lseek"); exit(2); } /* * Read and write stats located between two possible Linux restarts. * Activities that should be displayed are saved in id_seq[] array. */ for (i = 0; i < NR_ACT; i++) { if (!id_seq[i]) continue; if ((p = get_activity_position(act, id_seq[i])) < 0) { /* Should never happen */ PANIC(1); } if (!IS_SELECTED(act[p]->options)) continue; if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) { handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows, act[p]->id, &reset, file_actlst, from_file, &file_magic); } else { unsigned int optf, msk; optf = act[p]->opt_flags; for (msk = 1; msk < 0x10; msk <<= 1) { if (act[p]->opt_flags & msk) { act[p]->opt_flags &= msk; handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows, act[p]->id, &reset, file_actlst, from_file, &file_magic); act[p]->opt_flags = optf; } } } } if (!cnt) { /* Go to next Linux restart, if possible */ do { eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE, SOFT_SIZE); rtype = record_hdr[curr].record_type; if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) { read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr, file_actlst); } else if (!eosaf && (rtype == R_COMMENT)) { /* This was a COMMENT record: print it */ sar_print_special(curr, tm_start.use, tm_end.use, R_COMMENT, ifd, from_file, &file_magic); } } while (!eosaf && (rtype != R_RESTART)); } /* The last record we read was a RESTART one: Print it */ if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) { sar_print_special(curr, tm_start.use, tm_end.use, R_RESTART, ifd, from_file, &file_magic); } } while (!eosaf); close(ifd); free(file_actlst); }
PetscErrorCode maxIndSetAgg(IS perm,Mat Gmat,PetscBool strict_aggs,PetscInt verbose,PetscCoarsenData **a_locals_llist) { PetscErrorCode ierr; PetscBool isMPI; Mat_SeqAIJ *matA, *matB = 0; MPI_Comm wcomm = ((PetscObject)Gmat)->comm; Vec locState, ghostState; PetscInt num_fine_ghosts,kk,n,ix,j,*idx,*ii,iter,Iend,my0,nremoved; Mat_MPIAIJ *mpimat = 0; PetscScalar *cpcol_gid,*cpcol_state; PetscMPIInt mype,npe; const PetscInt *perm_ix; PetscInt nDone, nselected = 0; const PetscInt nloc = Gmat->rmap->n; PetscInt *lid_cprowID, *lid_gid; PetscBool *lid_removed; PetscScalar *lid_parent_gid = PETSC_NULL; /* only used for strict aggs */ PetscScalar *lid_state; PetscCoarsenData *agg_lists; PetscFunctionBegin; ierr = MPI_Comm_rank(wcomm, &mype);CHKERRQ(ierr); ierr = MPI_Comm_size(wcomm, &npe);CHKERRQ(ierr); /* get submatrices */ ierr = PetscObjectTypeCompare((PetscObject)Gmat, MATMPIAIJ, &isMPI);CHKERRQ(ierr); if (isMPI) { mpimat = (Mat_MPIAIJ*)Gmat->data; matA = (Mat_SeqAIJ*)mpimat->A->data; matB = (Mat_SeqAIJ*)mpimat->B->data; /* force compressed storage of B */ matB->compressedrow.check = PETSC_TRUE; ierr = MatCheckCompressedRow(mpimat->B,&matB->compressedrow,matB->i,Gmat->rmap->n,-1.0);CHKERRQ(ierr); assert(matB->compressedrow.use); } else { PetscBool isAIJ; ierr = PetscObjectTypeCompare((PetscObject)Gmat, MATSEQAIJ, &isAIJ);CHKERRQ(ierr); assert(isAIJ); matA = (Mat_SeqAIJ*)Gmat->data; } assert(matA && !matA->compressedrow.use); assert(matB==0 || matB->compressedrow.use); /* get vector */ ierr = MatGetVecs(Gmat, &locState, 0);CHKERRQ(ierr); ierr = MatGetOwnershipRange(Gmat,&my0,&Iend);CHKERRQ(ierr); if (mpimat) { PetscInt gid; for (kk=0,gid=my0;kk<nloc;kk++,gid++) { PetscScalar v = (PetscScalar)(gid); ierr = VecSetValues(locState, 1, &gid, &v, INSERT_VALUES);CHKERRQ(ierr); /* set with GID */ } ierr = VecAssemblyBegin(locState);CHKERRQ(ierr); ierr = VecAssemblyEnd(locState);CHKERRQ(ierr); ierr = VecScatterBegin(mpimat->Mvctx,locState,mpimat->lvec,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr); ierr = VecScatterEnd(mpimat->Mvctx,locState,mpimat->lvec,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr); ierr = VecGetArray(mpimat->lvec, &cpcol_gid);CHKERRQ(ierr); /* get proc ID in 'cpcol_gid' */ ierr = VecDuplicate(mpimat->lvec, &ghostState);CHKERRQ(ierr); /* need 2nd compressed col. of off proc data */ ierr = VecGetLocalSize(mpimat->lvec, &num_fine_ghosts);CHKERRQ(ierr); ierr = VecSet(ghostState, (PetscScalar)((PetscReal)NOT_DONE));CHKERRQ(ierr); /* set with UNKNOWN state */ } else num_fine_ghosts = 0; ierr = PetscMalloc(nloc*sizeof(PetscInt), &lid_cprowID);CHKERRQ(ierr); ierr = PetscMalloc((nloc+1)*sizeof(PetscInt), &lid_gid);CHKERRQ(ierr); /* explicit array needed */ ierr = PetscMalloc(nloc*sizeof(PetscBool), &lid_removed);CHKERRQ(ierr); /* explicit array needed */ if (strict_aggs) { ierr = PetscMalloc((nloc+1)*sizeof(PetscScalar), &lid_parent_gid);CHKERRQ(ierr); } ierr = PetscMalloc((nloc+1)*sizeof(PetscScalar), &lid_state);CHKERRQ(ierr); /* has ghost nodes for !strict and uses local indexing (yuck) */ ierr = PetscCDCreate(strict_aggs ? nloc : num_fine_ghosts+nloc, &agg_lists);CHKERRQ(ierr); if (a_locals_llist) *a_locals_llist = agg_lists; /* need an inverse map - locals */ for (kk=0;kk<nloc;kk++) { lid_cprowID[kk] = -1; lid_removed[kk] = PETSC_FALSE; if (strict_aggs) { lid_parent_gid[kk] = -1.0; } lid_gid[kk] = kk + my0; lid_state[kk] = (PetscScalar)((PetscReal)NOT_DONE); } /* set index into cmpressed row 'lid_cprowID' */ if (matB) { for (ix=0; ix<matB->compressedrow.nrows; ix++) { PetscInt lid = matB->compressedrow.rindex[ix]; lid_cprowID[lid] = ix; } } /* MIS */ iter = nremoved = nDone = 0; ierr = ISGetIndices(perm, &perm_ix);CHKERRQ(ierr); while (nDone < nloc || PETSC_TRUE) { /* asyncronous not implemented */ iter++; if (mpimat) { ierr = VecGetArray(ghostState, &cpcol_state);CHKERRQ(ierr); } /* check all vertices */ for (kk=0;kk<nloc;kk++){ PetscInt lid = perm_ix[kk]; NState state = (NState)PetscRealPart(lid_state[lid]); if (lid_removed[lid]) continue; if (state == NOT_DONE) { /* parallel test, delete if selected ghost */ PetscBool isOK = PETSC_TRUE; if ((ix=lid_cprowID[lid]) != -1) { /* if I have any ghost neighbors */ ii = matB->compressedrow.i; n = ii[ix+1] - ii[ix]; idx = matB->j + ii[ix]; for (j=0 ; j<n ; j++) { PetscInt cpid = idx[j]; /* compressed row ID in B mat */ PetscInt gid = (PetscInt)PetscRealPart(cpcol_gid[cpid]); NState statej = (NState)PetscRealPart(cpcol_state[cpid]); if (statej == NOT_DONE && gid >= Iend) { /* should be (pe>mype), use gid as pe proxy */ isOK = PETSC_FALSE; /* can not delete */ break; } else assert(!IS_SELECTED(statej)); /* lid is now deleted, do it */ } } /* parallel test */ if (isOK){ /* select or remove this vertex */ nDone++; /* check for singleton */ ii = matA->i; n = ii[lid+1] - ii[lid]; if (n < 2) { /* if I have any ghost adj then not a sing */ ix = lid_cprowID[lid]; if (ix==-1 || (matB->compressedrow.i[ix+1]-matB->compressedrow.i[ix])==0){ nremoved++; lid_removed[lid] = PETSC_TRUE; /* should select this because it is technically in the MIS but lets not */ /* lid_state[lid] = (PetscScalar)(lid+my0); */ continue; /* one local adj (me) and no ghost - singleton */ } } /* SELECTED state encoded with global index */ lid_state[lid] = (PetscScalar)(lid+my0); /* needed???? */ nselected++; if (strict_aggs) { ierr = PetscCDAppendID(agg_lists, lid, lid+my0);CHKERRQ(ierr); } else { ierr = PetscCDAppendID(agg_lists, lid, lid);CHKERRQ(ierr); } /* delete local adj */ idx = matA->j + ii[lid]; for (j=0; j<n; j++) { PetscInt lidj = idx[j]; NState statej = (NState)PetscRealPart(lid_state[lidj]); if (statej == NOT_DONE){ nDone++; /* id_llist[lidj] = id_llist[lid]; id_llist[lid] = lidj; */ /* insert 'lidj' into head of llist */ if (strict_aggs) { ierr = PetscCDAppendID(agg_lists, lid, lidj+my0);CHKERRQ(ierr); } else { ierr = PetscCDAppendID(agg_lists, lid, lidj);CHKERRQ(ierr); } lid_state[lidj] = (PetscScalar)(PetscReal)DELETED; /* delete this */ } } /* delete ghost adj of lid - deleted ghost done later for strict_aggs */ if (!strict_aggs) { if ((ix=lid_cprowID[lid]) != -1) { /* if I have any ghost neighbors */ ii = matB->compressedrow.i; n = ii[ix+1] - ii[ix]; idx = matB->j + ii[ix]; for (j=0 ; j<n ; j++) { PetscInt cpid = idx[j]; /* compressed row ID in B mat */ NState statej = (NState)PetscRealPart(cpcol_state[cpid]); assert(!IS_SELECTED(statej)); if (statej == NOT_DONE) { /* cpcol_state[cpid] = (PetscScalar)DELETED; this should happen later ... */ /* id_llist[lidj] = id_llist[lid]; id_llist[lid] = lidj; */ /* insert 'lidj' into head of llist */ ierr = PetscCDAppendID(agg_lists, lid, nloc+cpid);CHKERRQ(ierr); } } } } } /* selected */ } /* not done vertex */ } /* vertex loop */ /* update ghost states and count todos */ if (mpimat) { ierr = VecRestoreArray(ghostState, &cpcol_state);CHKERRQ(ierr); /* put lid state in 'locState' */ ierr = VecSetValues(locState, nloc, lid_gid, lid_state, INSERT_VALUES);CHKERRQ(ierr); ierr = VecAssemblyBegin(locState);CHKERRQ(ierr); ierr = VecAssemblyEnd(locState);CHKERRQ(ierr); /* scatter states, check for done */ ierr = VecScatterBegin(mpimat->Mvctx,locState,ghostState,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr); ierr = VecScatterEnd(mpimat->Mvctx,locState,ghostState,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr); /* delete locals from selected ghosts */ ierr = VecGetArray(ghostState, &cpcol_state);CHKERRQ(ierr); ii = matB->compressedrow.i; for (ix=0; ix<matB->compressedrow.nrows; ix++) { PetscInt lid = matB->compressedrow.rindex[ix]; /* local boundary node */ NState state = (NState)PetscRealPart(lid_state[lid]); if (state == NOT_DONE) { /* look at ghosts */ n = ii[ix+1] - ii[ix]; idx = matB->j + ii[ix]; for (j=0 ; j<n ; j++) { PetscInt cpid = idx[j]; /* compressed row ID in B mat */ NState statej = (NState)PetscRealPart(cpcol_state[cpid]); if (IS_SELECTED(statej)) { /* lid is now deleted, do it */ nDone++; lid_state[lid] = (PetscScalar)(PetscReal)DELETED; /* delete this */ if (!strict_aggs) { PetscInt lidj = nloc + cpid; /* id_llist[lid] = id_llist[lidj]; id_llist[lidj] = lid; */ /* insert 'lid' into head of ghost llist */ ierr = PetscCDAppendID(agg_lists, lidj, lid);CHKERRQ(ierr); } else { PetscInt sgid = (PetscInt)PetscRealPart(cpcol_gid[cpid]); lid_parent_gid[lid] = (PetscScalar)sgid; /* keep track of proc that I belong to */ } break; } } } } ierr = VecRestoreArray(ghostState, &cpcol_state);CHKERRQ(ierr); /* all done? */ { PetscInt t1, t2; t1 = nloc - nDone; assert(t1>=0); ierr = MPI_Allreduce(&t1, &t2, 1, MPIU_INT, MPI_SUM, wcomm);CHKERRQ(ierr); /* synchronous version */ if (t2 == 0) break; } } else break; /* all done */ } /* outer parallel MIS loop */ ierr = ISRestoreIndices(perm,&perm_ix);CHKERRQ(ierr); if (verbose) { if (verbose == 1) { ierr = PetscPrintf(wcomm,"\t[%d]%s removed %d of %d vertices. %d selected.\n",mype,__FUNCT__,nremoved,nloc,nselected);CHKERRQ(ierr); } else { ierr = MPI_Allreduce(&nremoved, &n, 1, MPIU_INT, MPI_SUM, wcomm);CHKERRQ(ierr); ierr = MatGetSize(Gmat, &kk, &j);CHKERRQ(ierr); ierr = MPI_Allreduce(&nselected, &j, 1, MPIU_INT, MPI_SUM, wcomm);CHKERRQ(ierr); ierr = PetscPrintf(wcomm,"\t[%d]%s removed %d of %d vertices. (%d local) %d selected.\n",mype,__FUNCT__,n,kk,nremoved,j);CHKERRQ(ierr); } } /* tell adj who my lid_parent_gid vertices belong to - fill in agg_lists selected ghost lists */ if (strict_aggs && matB) { PetscScalar *cpcol_sel_gid; PetscInt cpid,*icpcol_gid; /* need to copy this to free buffer -- should do this globaly */ ierr = PetscMalloc(num_fine_ghosts*sizeof(PetscInt), &icpcol_gid);CHKERRQ(ierr); for (cpid=0; cpid<num_fine_ghosts; cpid++) icpcol_gid[cpid] = (PetscInt)PetscRealPart(cpcol_gid[cpid]); /* get proc of deleted ghost */ ierr = VecSetValues(locState, nloc, lid_gid, lid_parent_gid, INSERT_VALUES);CHKERRQ(ierr); ierr = VecAssemblyBegin(locState);CHKERRQ(ierr); ierr = VecAssemblyEnd(locState);CHKERRQ(ierr); ierr = VecScatterBegin(mpimat->Mvctx,locState,mpimat->lvec,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr); ierr = VecScatterEnd(mpimat->Mvctx,locState,mpimat->lvec,INSERT_VALUES,SCATTER_FORWARD);CHKERRQ(ierr); ierr = VecGetArray(mpimat->lvec, &cpcol_sel_gid);CHKERRQ(ierr); /* has pe that owns ghost */ for (cpid=0; cpid<num_fine_ghosts; cpid++) { PetscInt sgid = (PetscInt)PetscRealPart(cpcol_sel_gid[cpid]); PetscInt gid = icpcol_gid[cpid]; if (sgid >= my0 && sgid < Iend) { /* I own this deleted */ PetscInt slid = sgid - my0; /* id_llist[lidj] = id_llist[lid]; id_llist[lid] = lidj; */ /* insert 'lidj' into head of llist */ ierr = PetscCDAppendID(agg_lists, slid, gid);CHKERRQ(ierr); assert(IS_SELECTED((NState)PetscRealPart(lid_state[slid]))); } } ierr = VecRestoreArray(mpimat->lvec, &cpcol_sel_gid);CHKERRQ(ierr); ierr = PetscFree(icpcol_gid);CHKERRQ(ierr); } else if (matB) { ierr = VecRestoreArray(mpimat->lvec, &cpcol_gid);CHKERRQ(ierr); } /* cache IS of removed nodes, use 'lid_gid' */ /* for (kk=n=0,ix=my0;kk<nloc;kk++,ix++) { */ /* if (lid_removed[kk]) lid_gid[n++] = ix; */ /* } */ /* assert(n==nremoved); */ /* ierr = PetscCDSetRemovedIS(agg_lists, wcomm, n, lid_gid);CHKERRQ(ierr); */ ierr = PetscFree(lid_cprowID);CHKERRQ(ierr); ierr = PetscFree(lid_gid);CHKERRQ(ierr); ierr = PetscFree(lid_removed);CHKERRQ(ierr); if (strict_aggs) { ierr = PetscFree(lid_parent_gid);CHKERRQ(ierr); } ierr = PetscFree(lid_state);CHKERRQ(ierr); if (mpimat){ ierr = VecDestroy(&ghostState);CHKERRQ(ierr); } ierr = VecDestroy(&locState);CHKERRQ(ierr); PetscFunctionReturn(0); }