/* *************************************************************************** * Read statistics sent by sadc, the data collector. *************************************************************************** */ void read_stats(void) { int curr = 1; unsigned long lines; unsigned int rows; int dis_hdr = 0; /* Don't buffer data if redirected to a pipe... */ setbuf(stdout, NULL); /* Read stats header */ read_header_data(); if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) { fprintf(stderr, _("Requested activities not available\n")); exit(1); } /* Determine if a stat line header has to be displayed */ dis_hdr = check_line_hdr(); lines = rows = get_win_height(); /* Perform required allocations */ allocate_structures(act); /* Print report header */ print_report_hdr(flags, &rectime, &file_hdr, act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr); /* Read system statistics sent by the data collector */ read_sadc_stat_bunch(0); if (!interval) { /* Display stats since boot time and exit */ write_stats_startup(0); } /* Save the first stats collected. Will be used to compute the average */ copy_structures(act, id_seq, record_hdr, 2, 0); /* Set a handler for SIGINT */ memset(&int_act, 0, sizeof(int_act)); int_act.sa_handler = int_handler; int_act.sa_flags = SA_RESTART; sigaction(SIGINT, &int_act, NULL); /* Main loop */ do { /* Get stats */ read_sadc_stat_bunch(curr); if (sigint_caught) { /* * SIGINT signal caught (it is sent by sadc). * => Display average stats. */ curr ^= 1; /* No data retrieved from last read */ break; } /* Print results */ if (!dis_hdr) { dis = lines / rows; if (dis) { lines %= rows; } lines++; } write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use, NO_RESET, ALL_ACTIVITIES, TRUE); if (record_hdr[curr].record_type == R_LAST_STATS) { /* File rotation is happening: Re-read header data sent by sadc */ read_header_data(); allocate_structures(act); } if (count > 0) { count--; } if (count) { curr ^= 1; } } while (count); /* * Print statistics average. * At least one line of stats must have been displayed for this. * (There may be no lines at all if we press Ctrl/C immediately). */ dis = dis_hdr; if (avg_count) { write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES); } }
/* *************************************************************************** * Read stats for current activity from file and display them. * * IN: * @ifd Input file descriptor. * @fpos Position in file where reading must start. * @curr Index in array for current sample statistics. * @rows Number of rows of screen. * @act_id Activity to display. * @file_actlst List of activities in file. * @file Name of file being read. * @file_magic file_magic structure filled with file magic header data. * * OUT: * @curr Index in array for next sample statistics. * @cnt Number of remaining lines of stats to write. * @eosaf Set to TRUE if EOF (end of file) has been reached. * @reset Set to TRUE if last_uptime variable should be * reinitialized (used in next_slice() function). *************************************************************************** */ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf, int rows, unsigned int act_id, int *reset, struct file_activity *file_actlst, char *file, struct file_magic *file_magic) { int p, reset_cd; unsigned long lines = 0; unsigned char rtype; int davg = 0, next, inc = -2; if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } /* * Restore the first stats collected. * Used to compute the rate displayed on the first line. */ copy_structures(act, id_seq, record_hdr, !*curr, 2); *cnt = count; /* Assess number of lines printed */ if ((p = get_activity_position(act, act_id)) >= 0) { if (act[p]->bitmap) { inc = count_bits(act[p]->bitmap->b_array, BITMAP_SIZE(act[p]->bitmap->b_size)); } else { inc = act[p]->nr; } } if (inc < 0) { /* Should never happen */ PANIC(inc); } reset_cd = 1; do { /* Display count lines of stats */ *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 the extra fields since it's not a special record */ read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst); } if ((lines >= rows) || !lines) { lines = 0; dis = 1; } else dis = 0; if (!*eosaf && (rtype != R_RESTART)) { if (rtype == R_COMMENT) { /* Display comment */ next = sar_print_special(*curr, tm_start.use, tm_end.use, R_COMMENT, ifd, file, file_magic); if (next) { /* A line of comment was actually displayed */ lines++; } continue; } /* next is set to 1 when we were close enough to desired interval */ next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use, *reset, act_id, reset_cd); reset_cd = 0; if (next && (*cnt > 0)) { (*cnt)--; } if (next) { davg++; *curr ^=1; lines += inc; } *reset = FALSE; } } while (*cnt && !*eosaf && (rtype != R_RESTART)); if (davg) { write_stats_avg(!*curr, USE_SA_FILE, act_id); } *reset = TRUE; }
/* *************************************************************************** * 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); }
/* *************************************************************************** * Read stats for current activity from file and write them. * * IN: * @ifd File descriptor of input file. * @fpos Position in file where reading must start. * @curr Index in array for current sample statistics. * @act_id Activity to display, or ~0 for all. * @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. * * OUT: * @curr Index in array for next sample statistics. * @cnt Number of lines of stats remaining to write. * @eosaf Set to TRUE if EOF (end of file) has been reached. * @reset Set to TRUE if last_uptime variable should be * reinitialized (used in next_slice() function). *************************************************************************** */ void rw_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf, unsigned int act_id, int *reset, struct file_activity *file_actlst, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime) { unsigned char rtype; int next; if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } if (DISPLAY_FIELD_LIST(fmt[f_position]->options)) { /* Print field list */ list_fields(act_id); } /* * Restore the first stats collected. * Used to compute the rate displayed on the first line. */ copy_structures(act, id_seq, record_hdr, !*curr, 2); *cnt = count; do { /* Display <count> lines of stats */ *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 the extra fields since it's not a RESTART record */ read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_nr_act, file_actlst); } if (!*eosaf && (rtype != R_RESTART)) { if (rtype == R_COMMENT) { sadf_print_special(*curr, tm_start.use, tm_end.use, R_COMMENT, ifd, rectime, loctime); continue; } next = write_parsable_stats(*curr, *reset, cnt, tm_start.use, tm_end.use, act_id, cpu_nr, rectime, loctime); if (next) { /* * next is set to 1 when we were close enough to desired interval. * In this case, the call to write_parsable_stats() has actually * displayed a line of stats. */ *curr ^=1; if (*cnt > 0) { (*cnt)--; } } *reset = FALSE; } } while (*cnt && !*eosaf && (rtype != R_RESTART)); *reset = TRUE; }
/* *************************************************************************** * Display activities for textual (XML-like) formats. * * IN: * @ifd File descriptor of input file. * @file_actlst List of (known or unknown) activities in file. * @dfile System activity data file name. * @file_magic System activity file magic header. * @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 textual_display_loop(int ifd, struct file_activity *file_actlst, char *dfile, struct file_magic *file_magic, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime) { int curr, tab = 0, rtype; int eosaf = TRUE, next, reset = FALSE; long cnt = 1; off_t fpos; /* Save current file position */ if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) { perror("lseek"); exit(2); } /* Print header (eg. XML file header) */ if (*fmt[f_position]->f_header) { (*fmt[f_position]->f_header)(&tab, F_BEGIN, dfile, file_magic, &file_hdr, cpu_nr, act, id_seq); } /* Process activities */ if (*fmt[f_position]->f_statistics) { (*fmt[f_position]->f_statistics)(&tab, F_BEGIN); } do { /* * If this record is a special (RESTART or COMMENT) one, * skip it and try to read the next record in file. */ do { eosaf = sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE); rtype = record_hdr[0].record_type; if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) { /* * 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); } if (!eosaf && (rtype == R_COMMENT)) { /* * Ignore COMMENT record. * (Unlike RESTART records, COMMENT records have an additional * comment field). */ if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) { perror("lseek"); } } } while (!eosaf && ((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); curr = 1; cnt = count; reset = TRUE; if (!eosaf) { 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 the extra fields since it's not a special record */ read_file_stat_bunch(act, curr, ifd, file_hdr.sa_nr_act, file_actlst); if (*fmt[f_position]->f_statistics) { (*fmt[f_position]->f_statistics)(&tab, F_MAIN); } /* next is set to 1 when we were close enough to desired interval */ next = write_textual_stats(curr, tm_start.use, tm_end.use, reset, &cnt, tab, cpu_nr, rectime, loctime); if (next) { curr ^= 1; if (cnt > 0) { cnt--; } } reset = FALSE; } if (!eosaf && (rtype == R_COMMENT)) { /* Ignore COMMENT record */ if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) { perror("lseek"); } } } while (cnt && !eosaf && (rtype != R_RESTART)); 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)) { /* Ignore COMMENT record */ if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) { perror("lseek"); } } } while (!eosaf && (rtype != R_RESTART)); } reset = TRUE; } } while (!eosaf); if (*fmt[f_position]->f_statistics) { (*fmt[f_position]->f_statistics)(&tab, F_END); } /* Rewind file */ if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } /* Process now RESTART entries to display restart messages */ if (*fmt[f_position]->f_restart) { (*fmt[f_position]->f_restart)(&tab, F_BEGIN, NULL, NULL, FALSE, &file_hdr); } do { if ((eosaf = sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE)) == 0) { rtype = record_hdr[0].record_type; if ((rtype != R_RESTART) && (rtype != R_COMMENT)) { read_file_stat_bunch(act, 0, ifd, file_hdr.sa_nr_act, file_actlst); } if (rtype == R_RESTART) { write_textual_restarts(0, tm_start.use, tm_end.use, tab, rectime, loctime); } else if (rtype == R_COMMENT) { /* Ignore COMMENT record */ if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) { perror("lseek"); } } } } while (!eosaf); if (*fmt[f_position]->f_restart) { (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL, FALSE, &file_hdr); } /* Rewind file */ if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } /* Last, process COMMENT entries to display comments */ if (DISPLAY_COMMENT(flags)) { if (*fmt[f_position]->f_comment) { (*fmt[f_position]->f_comment)(&tab, F_BEGIN, NULL, NULL, 0, NULL, &file_hdr); } do { if ((eosaf = sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE)) == 0) { rtype = record_hdr[0].record_type; if ((rtype != R_RESTART) && (rtype != R_COMMENT)) { read_file_stat_bunch(act, 0, ifd, file_hdr.sa_nr_act, file_actlst); } if (rtype == R_COMMENT) { write_textual_comments(0, tm_start.use, tm_end.use, tab, ifd, rectime, loctime); } } } while (!eosaf); if (*fmt[f_position]->f_comment) { (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL, &file_hdr); } } /* Print header trailer */ if (*fmt[f_position]->f_header) { (*fmt[f_position]->f_header)(&tab, F_END, dfile, file_magic, &file_hdr, cpu_nr, act, id_seq); } }
/* *************************************************************************** * 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 file contents in selected format (logic #1). * Logic #1: Grouped by record type. Sorted by timestamp. * Formats: XML, JSON * * IN: * @ifd File descriptor of input file. * @file_actlst List of (known or unknown) activities in file. * @file System activity data file name (name of file being read). * @file_magic System activity file magic header. * @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. *************************************************************************** */ void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file, struct file_magic *file_magic, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime) { int curr, tab = 0, rtype; int eosaf, next, reset = FALSE; __nr_t save_act_nr[NR_ACT] = {0}; long cnt = 1; off_t fpos; /* 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); /* Print header (eg. XML file header) */ if (*fmt[f_position]->f_header) { (*fmt[f_position]->f_header)(&tab, F_BEGIN, file, file_magic, &file_hdr, cpu_nr, act, id_seq); } /* Process activities */ if (*fmt[f_position]->f_statistics) { (*fmt[f_position]->f_statistics)(&tab, F_BEGIN); } do { /* * If this record is a special (RESTART or COMMENT) one, * skip it and try to read the next record in file. */ do { eosaf = read_next_sample(ifd, IGNORE_COMMENT | IGNORE_RESTART, 0, file, &rtype, tab, file_magic, file_actlst, rectime, loctime); } while (!eosaf && ((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); curr = 1; cnt = count; reset = TRUE; if (!eosaf) { do { eosaf = read_next_sample(ifd, IGNORE_COMMENT | IGNORE_RESTART, curr, file, &rtype, tab, file_magic, file_actlst, rectime, loctime); if (!eosaf && (rtype != R_COMMENT) && (rtype != R_RESTART)) { if (*fmt[f_position]->f_statistics) { (*fmt[f_position]->f_statistics)(&tab, F_MAIN); } /* next is set to 1 when we were close enough to desired interval */ next = generic_write_stats(curr, tm_start.use, tm_end.use, reset, &cnt, &tab, cpu_nr, rectime, loctime, FALSE, ALL_ACTIVITIES); if (next) { curr ^= 1; if (cnt > 0) { cnt--; } } reset = FALSE; } } while (cnt && !eosaf && (rtype != R_RESTART)); if (!cnt) { /* Go to next Linux restart, if possible */ do { eosaf = read_next_sample(ifd, IGNORE_COMMENT | IGNORE_RESTART, curr, file, &rtype, tab, file_magic, file_actlst, rectime, loctime); } while (!eosaf && (rtype != R_RESTART)); } reset = TRUE; } } while (!eosaf); if (*fmt[f_position]->f_statistics) { (*fmt[f_position]->f_statistics)(&tab, F_END); } /* Rewind file... */ if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } /* * ... and restore number of items for volatile activities * for this position in file. */ sr_act_nr(save_act_nr, DO_RESTORE); /* Process now RESTART entries to display restart messages */ if (*fmt[f_position]->f_restart) { (*fmt[f_position]->f_restart)(&tab, F_BEGIN, NULL, NULL, FALSE, &file_hdr, 0); } do { eosaf = read_next_sample(ifd, IGNORE_COMMENT, 0, file, &rtype, tab, file_magic, file_actlst, rectime, loctime); } while (!eosaf); if (*fmt[f_position]->f_restart) { (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL, FALSE, &file_hdr, 0); } /* Rewind file... */ if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } /* * ... and restore number of items for volatile activities * for this position in file. */ sr_act_nr(save_act_nr, DO_RESTORE); /* Last, process COMMENT entries to display comments */ if (DISPLAY_COMMENT(flags)) { if (*fmt[f_position]->f_comment) { (*fmt[f_position]->f_comment)(&tab, F_BEGIN, NULL, NULL, 0, NULL, &file_hdr); } do { eosaf = read_next_sample(ifd, IGNORE_RESTART, 0, file, &rtype, tab, file_magic, file_actlst, rectime, loctime); } while (!eosaf); if (*fmt[f_position]->f_comment) { (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL, &file_hdr); } } /* Print header trailer */ if (*fmt[f_position]->f_header) { (*fmt[f_position]->f_header)(&tab, F_END, file, file_magic, &file_hdr, cpu_nr, act, id_seq); } }
/* *************************************************************************** * Read stats for current activity from file and display its SVG graphs. * At most <count> lines of stats are taken into account. * * IN: * @ifd File descriptor of input file. * @fpos Position in file where reading must start. * @curr Index in array for current sample statistics. * @p Current activity position. * @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. * @save_act_nr Array where the number of volatile activities are saved * for current position in file. * @g_nr Number of graphs already displayed (for all activities). * * OUT: * @cnt Number of lines of stats remaining to write. * @eosaf Set to TRUE if EOF (end of file) has been reached. * @reset Set to TRUE if last_uptime variable should be * reinitialized (used in next_slice() function). * @g_nr Total number of graphs displayed (including current activity). *************************************************************************** */ void display_curr_act_graphs(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf, int p, int *reset, struct file_activity *file_actlst, __nr_t cpu_nr, struct tm *rectime, struct tm *loctime, char *file, struct file_magic *file_magic, __nr_t save_act_nr[], int *g_nr) { struct svg_parm parm; int rtype; int next, reset_cd; /* Rewind file... */ if (lseek(ifd, fpos, SEEK_SET) < fpos) { perror("lseek"); exit(2); } /* * ... and restore number of items for volatile activities * for this position in file. */ sr_act_nr(save_act_nr, DO_RESTORE); /* * Restore the first stats collected. * Used to compute the rate displayed on the first line. */ copy_structures(act, id_seq, record_hdr, !*curr, 2); parm.graph_no = *g_nr; parm.record_hdr = &record_hdr[2]; parm.restart = TRUE; *cnt = count; reset_cd = 1; /* Allocate graphs arrays */ (*act[p]->f_svg_print)(act[p], !*curr, F_BEGIN, &parm, 0, &record_hdr[!*curr]); do { *eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS, *curr, file, &rtype, 0, file_magic, file_actlst, rectime, loctime); if (!*eosaf && (rtype != R_COMMENT) && (rtype != R_RESTART)) { next = generic_write_stats(*curr, tm_start.use, tm_end.use, *reset, cnt, &parm, cpu_nr, rectime, loctime, reset_cd, act[p]->id); reset_cd = 0; if (next) { /* * next is set to 1 when we were close enough to desired interval. * In this case, the call to generic_write_stats() has actually * displayed a line of stats. */ parm.restart = FALSE; *curr ^= 1; if (*cnt > 0) { (*cnt)--; } } *reset = FALSE; } if (!*eosaf && (rtype == R_RESTART)) { parm.restart = TRUE; *reset = TRUE; /* Go to next statistics record, if possible */ do { *eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS, *curr, file, &rtype, 0, file_magic, file_actlst, rectime, loctime); } while (!*eosaf && ((rtype == R_RESTART) || (rtype == R_COMMENT))); *curr ^= 1; } } while (!*eosaf); *reset = TRUE; /* Actually display graphs for current activity */ (*act[p]->f_svg_print)(act[p], *curr, F_END, &parm, 0, &record_hdr[!*curr]); /* Update total number of graphs already displayed */ *g_nr = parm.graph_no; }
/* *************************************************************************** * 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); } }
/* *************************************************************************** * 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); }