#include "rbtree.h"
#include "jhash.h"
-static char blkparse_version[] = "1.0.2";
+static char blkparse_version[] = "1.2.0";
struct skip_info {
unsigned long start, end;
#define PPI_HASH_SHIFT (8)
#define PPI_HASH_SIZE (1 << PPI_HASH_SHIFT)
#define PPI_HASH_MASK (PPI_HASH_SIZE - 1)
+
+enum {
+ SORT_PROG_EVENT_N, /* Program Name */
+ SORT_PROG_EVENT_QKB, /* KB: Queued read and write */
+ SORT_PROG_EVENT_RKB, /* KB: Queued Read */
+ SORT_PROG_EVENT_WKB, /* KB: Queued Write */
+ SORT_PROG_EVENT_CKB, /* KB: Complete */
+ SORT_PROG_EVENT_QIO, /* IO: Queued read and write */
+ SORT_PROG_EVENT_RIO, /* IO: Queued Read */
+ SORT_PROG_EVENT_WIO, /* IO: Queued Write */
+ SORT_PROG_EVENT_CIO, /* IO: Complete */
+};
+
static struct per_process_info *ppi_hash_table[PPI_HASH_SIZE];
static struct per_process_info *ppi_list;
static int ppi_list_entries;
.flag = NULL,
.val = 's'
},
+ {
+ .name = "sort-program-stats",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'S'
+ },
{
.name = "track-ios",
.has_arg = no_argument,
static unsigned long read_sequence;
static int per_process_stats;
+static int per_process_stats_event = SORT_PROG_EVENT_N;
static int per_device_and_cpu_stats = 1;
static int track_ios;
static int ppi_hash_by_pid = 1;
ppm = malloc(sizeof(*ppm));
memset(ppm, 0, sizeof(*ppm));
ppm->pid = pid;
- strcpy(ppm->comm, name);
+ memset(ppm->comm, 0, sizeof(ppm->comm));
+ strncpy(ppm->comm, name, sizeof(ppm->comm));
+ ppm->comm[sizeof(ppm->comm) - 1] = '\0';
ppm->hash_next = ppm_hash_table[hash_idx];
ppm_hash_table[hash_idx] = ppm;
}
if (rw) {
ios->mwrites++;
ios->mwrite_kb += t_kb(t);
+ ios->mwrite_b += t_b(t);
} else {
ios->mreads++;
ios->mread_kb += t_kb(t);
+ ios->mread_b += t_b(t);
}
}
if (rw) {
ios->qwrites_pc++;
ios->qwrite_kb_pc += t_kb(t);
+ ios->qwrite_b_pc += t_b(t);
} else {
ios->qreads_pc++;
ios->qread_kb += t_kb(t);
+ ios->qread_b_pc += t_b(t);
}
}
if (rw) {
ios->iwrites_pc++;
ios->iwrite_kb_pc += bytes >> 10;
+ ios->iwrite_b_pc += bytes & 1023;
} else {
ios->ireads_pc++;
ios->iread_kb_pc += bytes >> 10;
+ ios->iread_b_pc += bytes & 1023;
}
}
if (rw) {
ios->wrqueue_pc++;
ios->iwrite_kb_pc -= t_kb(t);
+ ios->iwrite_b_pc -= t_b(t);
} else {
ios->rrqueue_pc++;
ios->iread_kb_pc -= t_kb(t);
+ ios->iread_b_pc -= t_b(t);
}
}
if (rw) {
ios->qwrites++;
ios->qwrite_kb += t_kb(t);
+ ios->qwrite_b += t_b(t);
} else {
ios->qreads++;
ios->qread_kb += t_kb(t);
+ ios->qread_b += t_b(t);
}
}
if (rw) {
ios->cwrites++;
ios->cwrite_kb += bytes >> 10;
+ ios->cwrite_b += bytes & 1023;
} else {
ios->creads++;
ios->cread_kb += bytes >> 10;
+ ios->cread_b += bytes & 1023;
}
}
if (rw) {
ios->iwrites++;
ios->iwrite_kb += bytes >> 10;
+ ios->iwrite_b += bytes & 1023;
} else {
ios->ireads++;
ios->iread_kb += bytes >> 10;
+ ios->iread_b += bytes & 1023;
}
}
if (rw) {
ios->wrqueue++;
ios->iwrite_kb -= t_kb(t);
+ ios->iwrite_b -= t_b(t);
} else {
ios->rrqueue++;
ios->iread_kb -= t_kb(t);
+ ios->iread_b -= t_b(t);
}
}
fprintf(ofp, "%s\n", msg);
- fprintf(ofp, " Reads Queued: %s, %siB\t", size_cnv(x, ios->qreads, 0), size_cnv(y, ios->qread_kb, 1));
- fprintf(ofp, " Writes Queued: %s, %siB\n", size_cnv(x, ios->qwrites, 0), size_cnv(y, ios->qwrite_kb, 1));
- fprintf(ofp, " Read Dispatches: %s, %siB\t", size_cnv(x, ios->ireads, 0), size_cnv(y, ios->iread_kb, 1));
- fprintf(ofp, " Write Dispatches: %s, %siB\n", size_cnv(x, ios->iwrites, 0), size_cnv(y, ios->iwrite_kb, 1));
+ fprintf(ofp, " Reads Queued: %s, %siB\t",
+ size_cnv(x, ios->qreads, 0),
+ size_cnv(y, ios->qread_kb + (ios->qread_b>>10), 1));
+ fprintf(ofp, " Writes Queued: %s, %siB\n",
+ size_cnv(x, ios->qwrites, 0),
+ size_cnv(y, ios->qwrite_kb + (ios->qwrite_b>>10), 1));
+ fprintf(ofp, " Read Dispatches: %s, %siB\t",
+ size_cnv(x, ios->ireads, 0),
+ size_cnv(y, ios->iread_kb + (ios->iread_b>>10), 1));
+ fprintf(ofp, " Write Dispatches: %s, %siB\n",
+ size_cnv(x, ios->iwrites, 0),
+ size_cnv(y, ios->iwrite_kb + (ios->iwrite_b>>10), 1));
fprintf(ofp, " Reads Requeued: %s\t\t", size_cnv(x, ios->rrqueue, 0));
fprintf(ofp, " Writes Requeued: %s\n", size_cnv(x, ios->wrqueue, 0));
- fprintf(ofp, " Reads Completed: %s, %siB\t", size_cnv(x, ios->creads, 0), size_cnv(y, ios->cread_kb, 1));
- fprintf(ofp, " Writes Completed: %s, %siB\n", size_cnv(x, ios->cwrites, 0), size_cnv(y, ios->cwrite_kb, 1));
- fprintf(ofp, " Read Merges: %s, %siB\t", size_cnv(x, ios->mreads, 0), size_cnv(y, ios->mread_kb, 1));
- fprintf(ofp, " Write Merges: %s, %siB\n", size_cnv(x, ios->mwrites, 0), size_cnv(y, ios->mwrite_kb, 1));
+ fprintf(ofp, " Reads Completed: %s, %siB\t",
+ size_cnv(x, ios->creads, 0),
+ size_cnv(y, ios->cread_kb + (ios->cread_b>>10), 1));
+ fprintf(ofp, " Writes Completed: %s, %siB\n",
+ size_cnv(x, ios->cwrites, 0),
+ size_cnv(y, ios->cwrite_kb + (ios->cwrite_b>>10), 1));
+ fprintf(ofp, " Read Merges: %s, %siB\t",
+ size_cnv(x, ios->mreads, 0),
+ size_cnv(y, ios->mread_kb + (ios->mread_b>>10), 1));
+ fprintf(ofp, " Write Merges: %s, %siB\n",
+ size_cnv(x, ios->mwrites, 0),
+ size_cnv(y, ios->mwrite_kb + (ios->mwrite_b>>10), 1));
if (pdi) {
fprintf(ofp, " Read depth: %'8u%8c\t", pdi->max_depth[0], ' ');
fprintf(ofp, " Write depth: %'8u\n", pdi->max_depth[1]);
}
if (ios->qreads_pc || ios->qwrites_pc || ios->ireads_pc || ios->iwrites_pc ||
ios->rrqueue_pc || ios->wrqueue_pc || ios->creads_pc || ios->cwrites_pc) {
- fprintf(ofp, " PC Reads Queued: %s, %siB\t", size_cnv(x, ios->qreads_pc, 0), size_cnv(y, ios->qread_kb_pc, 1));
- fprintf(ofp, " PC Writes Queued: %s, %siB\n", size_cnv(x, ios->qwrites_pc, 0), size_cnv(y, ios->qwrite_kb_pc, 1));
- fprintf(ofp, " PC Read Disp.: %s, %siB\t", size_cnv(x, ios->ireads_pc, 0), size_cnv(y, ios->iread_kb_pc, 1));
- fprintf(ofp, " PC Write Disp.: %s, %siB\n", size_cnv(x, ios->iwrites_pc, 0), size_cnv(y, ios->iwrite_kb_pc, 1));
+ fprintf(ofp, " PC Reads Queued: %s, %siB\t",
+ size_cnv(x, ios->qreads_pc, 0),
+ size_cnv(y,
+ ios->qread_kb_pc + (ios->qread_b_pc>>10), 1));
+ fprintf(ofp, " PC Writes Queued: %s, %siB\n",
+ size_cnv(x, ios->qwrites_pc, 0),
+ size_cnv(y,
+ ios->qwrite_kb_pc + (ios->qwrite_b_pc>>10), 1));
+ fprintf(ofp, " PC Read Disp.: %s, %siB\t",
+ size_cnv(x, ios->ireads_pc, 0),
+ size_cnv(y,
+ ios->iread_kb_pc + (ios->iread_b_pc>>10), 1));
+ fprintf(ofp, " PC Write Disp.: %s, %siB\n",
+ size_cnv(x, ios->iwrites_pc, 0),
+ size_cnv(y,
+ ios->iwrite_kb_pc + (ios->iwrite_b_pc>>10),
+ 1));
fprintf(ofp, " PC Reads Req.: %s\t\t", size_cnv(x, ios->rrqueue_pc, 0));
fprintf(ofp, " PC Writes Req.: %s\n", size_cnv(x, ios->wrqueue_pc, 0));
fprintf(ofp, " PC Reads Compl.: %s\t\t", size_cnv(x, ios->creads_pc, 0));
- fprintf(ofp, " PC Writes Compl.: %s\n", size_cnv(x, ios->cwrites, 0));
+ fprintf(ofp, " PC Writes Compl.: %s\n", size_cnv(x, ios->cwrites_pc, 0));
}
fprintf(ofp, " IO unplugs: %'8lu%8c\t", ios->io_unplugs, ' ');
fprintf(ofp, " Timer unplugs: %'8lu\n", ios->timer_unplugs);
return res;
}
+static int ppi_event_compare(const void *p1, const void *p2)
+{
+ struct per_process_info *ppi1 = *((struct per_process_info **) p1);
+ struct per_process_info *ppi2 = *((struct per_process_info **) p2);
+ struct io_stats *ios1 = &ppi1->io_stats;
+ struct io_stats *ios2 = &ppi2->io_stats;
+ unsigned long io1, io2;
+ unsigned long long kb1,kb2;
+ int sort_by_kb = 1;
+
+ io1 = io2 = 0;
+ kb1 = kb2 = 0;
+
+ switch (per_process_stats_event) {
+ case SORT_PROG_EVENT_QKB: /* KB: Queued read and write */
+ kb1 = ios1->qwrite_kb + (ios1->qwrite_b>>10) +
+ ios1->qread_kb + (ios1->qread_b>>10);
+ kb2 = ios2->qwrite_kb + (ios2->qwrite_b>>10) +
+ ios2->qread_kb + (ios2->qread_b>>10);
+ break;
+ case SORT_PROG_EVENT_RKB: /* KB: Queued Read */
+ kb1 = ios1->qread_kb + (ios1->qread_b>>10);
+ kb2 = ios2->qread_kb + (ios2->qread_b>>10);
+ break;
+ case SORT_PROG_EVENT_WKB: /* KB: Queued Write */
+ kb1 = ios1->qwrite_kb + (ios1->qwrite_b>>10);
+ kb2 = ios2->qwrite_kb + (ios2->qwrite_b>>10);
+ break;
+ case SORT_PROG_EVENT_CKB: /* KB: Complete */
+ kb1 = ios1->cwrite_kb + (ios1->cwrite_b>>10) +
+ ios1->cread_kb + (ios1->cread_b>>10);
+ kb2 = ios2->cwrite_kb + (ios2->cwrite_b>>10) +
+ ios2->cread_kb + (ios2->cread_b>>10);
+ break;
+ case SORT_PROG_EVENT_QIO: /* IO: Queued read and write */
+ sort_by_kb = 0;
+ io1 = ios1->qreads + ios1->qwrites;
+ io2 = ios2->qreads + ios2->qwrites;
+ break;
+ case SORT_PROG_EVENT_RIO: /* IO: Queued Read */
+ sort_by_kb = 0;
+ io1 = ios1->qreads;
+ io2 = ios2->qreads;
+ break;
+ case SORT_PROG_EVENT_WIO: /* IO: Queued Write */
+ sort_by_kb = 0;
+ io1 = ios1->qwrites;
+ io2 = ios2->qwrites;
+ break;
+ case SORT_PROG_EVENT_CIO: /* IO: Complete */
+ sort_by_kb = 0;
+ io1 = ios1->creads + ios1->cwrites;
+ io2 = ios2->creads + ios2->cwrites;
+ break;
+ }
+
+
+ /* compare kb */
+ if (sort_by_kb) {
+ if (kb1 > kb2)
+ return 1;
+ else if (kb1 == kb2)
+ return 0;
+ return -1;
+ }
+
+ /* compare io */
+ if (io1 > io2)
+ return 1;
+ else if (io1 == io2)
+ return 0;
+ return -1;
+}
+
+static int ppi_compare(const void *p1, const void *p2)
+{
+ if (per_process_stats_event == SORT_PROG_EVENT_N)
+ return ppi_name_compare(p1, p2);
+
+ return ppi_event_compare(p1, p2);
+}
+
static void sort_process_list(void)
{
struct per_process_info **ppis;
ppi = ppi->list_next;
}
- qsort(ppis, ppi_list_entries, sizeof(ppi), ppi_name_compare);
+ qsort(ppis, ppi_list_entries, sizeof(ppi), ppi_compare);
i = ppi_list_entries - 1;
ppi_list = NULL;
total.iwrite_kb += ios->iwrite_kb;
total.mread_kb += ios->mread_kb;
total.mwrite_kb += ios->mwrite_kb;
+ total.qread_b += ios->qread_b;
+ total.qwrite_b += ios->qwrite_b;
+ total.cread_b += ios->cread_b;
+ total.cwrite_b += ios->cwrite_b;
+ total.iread_b += ios->iread_b;
+ total.iwrite_b += ios->iwrite_b;
+ total.mread_b += ios->mread_b;
+ total.mwrite_b += ios->mwrite_b;
total.qreads_pc += ios->qreads_pc;
total.qwrites_pc += ios->qwrites_pc;
total.qwrite_kb_pc += ios->qwrite_kb_pc;
total.iread_kb_pc += ios->iread_kb_pc;
total.iwrite_kb_pc += ios->iwrite_kb_pc;
+ total.qread_b_pc += ios->qread_b_pc;
+ total.qwrite_b_pc += ios->qwrite_b_pc;
+ total.iread_b_pc += ios->iread_b_pc;
+ total.iwrite_b_pc += ios->iwrite_b_pc;
total.timer_unplugs += ios->timer_unplugs;
total.io_unplugs += ios->io_unplugs;
wrate = rrate = 0;
msec = (pdi->last_reported_time - pdi->first_reported_time) / 1000000;
if (msec) {
- rrate = 1000 * total.cread_kb / msec;
- wrate = 1000 * total.cwrite_kb / msec;
+ rrate = ((1000 * total.cread_kb) + total.cread_b) /
+ msec;
+ wrate = ((1000 * total.cwrite_kb) + total.cwrite_b) /
+ msec;
}
fprintf(ofp, "\nThroughput (R/W): %'LuKiB/s / %'LuKiB/s\n",
}
}
+static void correct_abs_start_time(void)
+{
+ long delta = genesis_time - start_timestamp;
+
+ abs_start_time.tv_sec += SECONDS(delta);
+ abs_start_time.tv_nsec += NANO_SECONDS(delta);
+ if (abs_start_time.tv_nsec < 0) {
+ abs_start_time.tv_nsec += 1000000000;
+ abs_start_time.tv_sec -= 1;
+ } else
+ if (abs_start_time.tv_nsec > 1000000000) {
+ abs_start_time.tv_nsec -= 1000000000;
+ abs_start_time.tv_sec += 1;
+ }
+}
+
static void find_genesis(void)
{
struct trace *t = trace_list;
*/
if (start_timestamp
&& start_timestamp != genesis_time) {
- long delta = genesis_time - start_timestamp;
-
- abs_start_time.tv_sec += SECONDS(delta);
- abs_start_time.tv_nsec += NANO_SECONDS(delta);
- if (abs_start_time.tv_nsec < 0) {
- abs_start_time.tv_nsec += 1000000000;
- abs_start_time.tv_sec -= 1;
- } else
- if (abs_start_time.tv_nsec > 1000000000) {
- abs_start_time.tv_nsec -= 1000000000;
- abs_start_time.tv_sec += 1;
- }
+ correct_abs_start_time();
}
}
* create a map of the cpus we have traces for
*/
cpu_map = malloc(pdi->cpu_map_max / sizeof(long));
+ memset(cpu_map, 0, sizeof(*cpu_map));
n = rb_first(&rb_sort_root);
while (n) {
__t = rb_entry(n, struct trace, rb_node);
return 0;
}
-#define S_OPTS "a:A:b:D:d:f:F:hi:o:Oqstw:vVM"
+static int get_program_sort_event(const char *str)
+{
+ char evt = str[0];
+
+ switch (evt) {
+ case 'N':
+ per_process_stats_event = SORT_PROG_EVENT_N;
+ break;
+ case 'Q':
+ per_process_stats_event = SORT_PROG_EVENT_QKB;
+ break;
+ case 'q':
+ per_process_stats_event = SORT_PROG_EVENT_QIO;
+ break;
+ case 'R':
+ per_process_stats_event = SORT_PROG_EVENT_RKB;
+ break;
+ case 'r':
+ per_process_stats_event = SORT_PROG_EVENT_RIO;
+ break;
+ case 'W':
+ per_process_stats_event = SORT_PROG_EVENT_WKB;
+ break;
+ case 'w':
+ per_process_stats_event = SORT_PROG_EVENT_WIO;
+ break;
+ case 'C':
+ per_process_stats_event = SORT_PROG_EVENT_CKB;
+ break;
+ case 'c':
+ per_process_stats_event = SORT_PROG_EVENT_CIO;
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+#define S_OPTS "a:A:b:D:d:f:F:hi:o:OqsS:tw:vVM"
static char usage_str[] = "\n\n" \
"-i <file> | --input=<file>\n" \
"[ -a <action field> | --act-mask=<action field> ]\n" \
"[ -O | --no-text-output ]\n" \
"[ -q | --quiet ]\n" \
"[ -s | --per-program-stats ]\n" \
+ "[ -S <event> | --sort-program-stats=<event> ]\n" \
"[ -t | --track-ios ]\n" \
"[ -w <time> | --stopwatch=<time> ]\n" \
"[ -M | --no-msgs\n" \
"\t-O Do NOT output text data\n" \
"\t-q Quiet. Don't display any stats at the end of the trace\n" \
"\t-s Show per-program io statistics\n" \
+ "\t-S Show per-program io statistics sorted by N/Q/q/R/r/W/w/C/c\n" \
+ "\t N:Name, Q/q:Queued(read & write), R/r:Queued Read, W/w:Queued Write, C/c:Complete.\n" \
+ "\t Sort programs by how much data(KB): Q,R,W,C.\n" \
+ "\t Sort programs by how many IO operations: q,r,w,c.\n" \
+ "\t if -S was used, the -s parameter will be ignored.\n" \
"\t-t Track individual ios. Will tell you the time a request took\n" \
"\t to get queued, to get dispatched, and to get completed\n" \
"\t-w Only parse data between the given time interval in seconds.\n" \
case 's':
per_process_stats = 1;
break;
+ case 'S':
+ per_process_stats = 1;
+ if (get_program_sort_event(optarg))
+ return 1;
+ break;
case 't':
track_ios = 1;
break;
ofp = fdopen(STDOUT_FILENO, "w");
mode = _IOLBF;
} else {
- char ofname[128];
+ char ofname[PATH_MAX];
snprintf(ofname, sizeof(ofname) - 1, "%s", output_name);
ofp = fopen(ofname, "w");