Home | History | Annotate | Download | only in ioshark
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <sys/time.h>
     19 #include <sys/types.h>
     20 #include <unistd.h>
     21 #include <stdlib.h>
     22 #include <signal.h>
     23 #include <string.h>
     24 #include <sys/stat.h>
     25 #include <sys/errno.h>
     26 #include <fcntl.h>
     27 #include <string.h>
     28 #include <assert.h>
     29 #include <sys/vfs.h>
     30 #include <sys/statvfs.h>
     31 #include <sys/mman.h>
     32 #include <inttypes.h>
     33 #include "ioshark.h"
     34 #include "ioshark_bench.h"
     35 #define _BSD_SOURCE
     36 #include <endian.h>
     37 
     38 extern char *progname;
     39 extern int verbose, summary_mode;
     40 
     41 void *
     42 files_db_create_handle(void)
     43 {
     44 	struct files_db_handle *h;
     45 	int i;
     46 
     47 	h = malloc(sizeof(struct files_db_handle));
     48 	for (i = 0 ; i < FILE_DB_HASHSIZE ; i++)
     49 		h->files_db_buckets[i] = NULL;
     50 	return h;
     51 }
     52 
     53 void *files_db_lookup_byfileno(void *handle, int fileno)
     54 {
     55 	u_int32_t	hash;
     56 	struct files_db_handle *h = (struct files_db_handle *)handle;
     57 	struct files_db_s *db_node;
     58 
     59 	hash = fileno % FILE_DB_HASHSIZE;
     60 	db_node = h->files_db_buckets[hash];
     61 	while (db_node != NULL) {
     62 		if (db_node->fileno == fileno)
     63 			break;
     64 		db_node = db_node->next;
     65 	}
     66 	return db_node;
     67 }
     68 
     69 void *files_db_add_byfileno(void *handle, int fileno, int readonly)
     70 {
     71 	u_int32_t	hash = fileno % FILE_DB_HASHSIZE;
     72 	struct files_db_handle *h = (struct files_db_handle *)handle;
     73 	struct files_db_s *db_node;
     74 
     75 	db_node = (struct files_db_s *)
     76 		files_db_lookup_byfileno(handle, fileno);
     77 	if (db_node == NULL) {
     78 		db_node = malloc(sizeof(struct files_db_s));
     79 		db_node->fileno = fileno;
     80 		db_node->filename = NULL;
     81 		db_node->readonly = readonly;
     82 		db_node->size = 0;
     83 		db_node->fd = -1;
     84 		db_node->next = h->files_db_buckets[hash];
     85 		h->files_db_buckets[hash] = db_node;
     86 	} else {
     87 		fprintf(stderr,
     88 			"%s: Node to be added already exists fileno = %d\n\n",
     89 			__func__, fileno);
     90 		exit(EXIT_FAILURE);
     91 	}
     92 	return db_node;
     93 }
     94 
     95 void
     96 files_db_fsync_discard_files(void *handle)
     97 {
     98 	struct files_db_handle *h = (struct files_db_handle *)handle;
     99 	struct files_db_s *db_node;
    100 	int i;
    101 
    102 	for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
    103 		db_node = h->files_db_buckets[i];
    104 		while (db_node != NULL) {
    105 			int do_close = 0;
    106 
    107 			if (db_node->fd == -1) {
    108 				int fd;
    109 				int openflags;
    110 
    111 				/*n
    112 				 * File was closed, let's open it so we can
    113 				 * fsync and fadvise(DONTNEED) it.
    114 				 */
    115 				do_close = 1;
    116 				if (files_db_readonly(db_node))
    117 					openflags = O_RDONLY;
    118 				else
    119 					openflags = O_RDWR;
    120 				fd = open(files_db_get_filename(db_node),
    121 					  openflags);
    122 				if (fd < 0) {
    123 					fprintf(stderr,
    124 						"%s: open(%s %x) error %d\n",
    125 						progname, db_node->filename,
    126 						openflags,
    127 						errno);
    128 					exit(EXIT_FAILURE);
    129 				}
    130 				db_node->fd = fd;
    131 			}
    132 			if (!db_node->readonly && fsync(db_node->fd) < 0) {
    133 				fprintf(stderr, "%s: Cannot fsync %s\n",
    134 					__func__, db_node->filename);
    135 				exit(1);
    136 			}
    137 			if (posix_fadvise(db_node->fd, 0, 0,
    138 					  POSIX_FADV_DONTNEED) < 0) {
    139 				fprintf(stderr,
    140 					"%s: Cannot fadvise(DONTNEED) %s\n",
    141 					__func__, db_node->filename);
    142 				exit(1);
    143 			}
    144 			if (do_close) {
    145 				close(db_node->fd);
    146 				db_node->fd = -1;
    147 			}
    148 			db_node = db_node->next;
    149 		}
    150 	}
    151 }
    152 
    153 void
    154 files_db_update_fd(void *node, int fd)
    155 {
    156 	struct files_db_s *db_node = (struct files_db_s *)node;
    157 
    158 	db_node->fd = fd;
    159 }
    160 
    161 void
    162 files_db_close_fd(void *node)
    163 {
    164 	struct files_db_s *db_node = (struct files_db_s *)node;
    165 
    166 	if (db_node->fd != -1)
    167 		close(db_node->fd);
    168 	db_node->fd = -1;
    169 }
    170 
    171 void
    172 files_db_close_files(void *handle)
    173 {
    174 	struct files_db_handle *h = (struct files_db_handle *)handle;
    175 	struct files_db_s *db_node;
    176 	int i;
    177 
    178 	for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
    179 		db_node = h->files_db_buckets[i];
    180 		while (db_node != NULL) {
    181 			if ((db_node->fd != -1) && close(db_node->fd) < 0) {
    182 				fprintf(stderr, "%s: Cannot close %s\n",
    183 					__func__, db_node->filename);
    184 				exit(1);
    185 			}
    186 			db_node->fd = -1;
    187 			db_node = db_node->next;
    188 		}
    189 	}
    190 }
    191 
    192 void
    193 files_db_unlink_files(void *handle)
    194 {
    195 	struct files_db_handle *h = (struct files_db_handle *)handle;
    196 	struct files_db_s *db_node;
    197 	int i;
    198 
    199 	for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
    200 		db_node = h->files_db_buckets[i];
    201 		while (db_node != NULL) {
    202 			if ((db_node->fd != -1) && close(db_node->fd) < 0) {
    203 				fprintf(stderr, "%s: Cannot close %s\n",
    204 					__func__, db_node->filename);
    205 				exit(1);
    206 			}
    207 			db_node->fd = -1;
    208 			if (is_readonly_mount(db_node->filename, db_node->size) == 0) {
    209 				if (unlink(db_node->filename) < 0) {
    210 					fprintf(stderr, "%s: Cannot unlink %s:%s\n",
    211 						__func__, db_node->filename,
    212 						strerror(errno));
    213 					exit(EXIT_FAILURE);
    214 				}
    215 			}
    216 			db_node = db_node->next;
    217 		}
    218 	}
    219 }
    220 
    221 void
    222 files_db_free_memory(void *handle)
    223 {
    224 	struct files_db_handle *h = (struct files_db_handle *)handle;
    225 	struct files_db_s *db_node, *tmp;
    226 	int i;
    227 
    228 	for (i = 0 ; i < FILE_DB_HASHSIZE ; i++) {
    229 		db_node = h->files_db_buckets[i];
    230 		while (db_node != NULL) {
    231 			tmp = db_node;
    232 			db_node = db_node->next;
    233 			free(tmp->filename);
    234 			free(tmp);
    235 		}
    236 	}
    237 	free(h);
    238 }
    239 
    240 char *
    241 get_buf(char **buf, int *buflen, int len, int do_fill __attribute__((unused)))
    242 {
    243 	if (len == 0 && *buf == NULL) {
    244 		/*
    245 		 * If we ever get a zero len
    246 		 * request, start with MINBUFLEN
    247 		 */
    248 		if (*buf == NULL)
    249 			len = MINBUFLEN / 2;
    250 	}
    251 	if (*buflen < len) {
    252 		*buflen = MAX(MINBUFLEN, len * 2);
    253 		if (*buf)
    254 			free(*buf);
    255 		*buf = malloc(*buflen);
    256 		if (do_fill) {
    257 			u_int32_t *s;
    258 			int count;
    259 
    260 			s = (u_int32_t *)*buf;
    261 			count = *buflen / sizeof(u_int32_t);
    262 			while (count > 0) {
    263 				*s++ = rand();
    264 				count--;
    265 			}
    266 		}
    267 	}
    268 	assert(*buf != NULL);
    269 	return *buf;
    270 }
    271 
    272 void
    273 create_file(char *path, size_t size, struct rw_bytes_s *rw_bytes)
    274 {
    275 	int fd, n;
    276 	char *buf = NULL;
    277 	int buflen = 0;
    278 
    279 	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    280 	if (fd < 0) {
    281 		fprintf(stderr, "%s Cannot create file %s, error = %d\n",
    282 			progname, path, errno);
    283 		exit(EXIT_FAILURE);
    284 	}
    285 	while (size > 0) {
    286 		n = MIN(size, MINBUFLEN);
    287 		buf = get_buf(&buf, &buflen, n, 1);
    288 		if (write(fd, buf, n) < n) {
    289 			fprintf(stderr,
    290 				"%s Cannot write file %s, error = %d\n",
    291 				progname, path, errno);
    292 			exit(EXIT_FAILURE);
    293 		}
    294 		rw_bytes->bytes_written += n;
    295 		size -= n;
    296 	}
    297 	if (fsync(fd) < 0) {
    298 		fprintf(stderr, "%s Cannot fsync file %s, error = %d\n",
    299 			progname, path, errno);
    300 		exit(EXIT_FAILURE);
    301 	}
    302 	if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) < 0) {
    303 		fprintf(stderr,
    304 			"%s Cannot fadvise(DONTNEED) file %s, error = %d\n",
    305 			progname, path, errno);
    306 		exit(EXIT_FAILURE);
    307 	}
    308 	close(fd);
    309 }
    310 
    311 void
    312 print_op_stats(u_int64_t *op_counts)
    313 {
    314 	int i;
    315 	extern char *IO_op[];
    316 
    317 	printf("IO Operation counts :\n");
    318 	for (i = IOSHARK_LSEEK ; i < IOSHARK_MAX_FILE_OP ; i++) {
    319 		printf("%s: %ju\n",
    320 		       IO_op[i], op_counts[i]);
    321 	}
    322 }
    323 
    324 void
    325 print_bytes(char *desc, struct rw_bytes_s *rw_bytes)
    326 {
    327 	if (!summary_mode)
    328 		printf("%s: Reads = %dMB, Writes = %dMB\n",
    329 		       desc,
    330 		       (int)(rw_bytes->bytes_read / (1024 * 1024)),
    331 		       (int)(rw_bytes->bytes_written / (1024 * 1024)));
    332 	else
    333 		printf("%d %d ",
    334 		       (int)(rw_bytes->bytes_read / (1024 * 1024)),
    335 		       (int)(rw_bytes->bytes_written / (1024 * 1024)));
    336 }
    337 
    338 struct cpu_disk_util_stats {
    339 	/* CPU util */
    340 	u_int64_t user_cpu_ticks;
    341 	u_int64_t nice_cpu_ticks;
    342 	u_int64_t system_cpu_ticks;
    343 	u_int64_t idle_cpu_ticks;
    344 	u_int64_t iowait_cpu_ticks;
    345 	u_int64_t hardirq_cpu_ticks;
    346 	u_int64_t softirq_cpu_ticks;
    347 	/* disk util */
    348 	unsigned long long uptime;
    349 	unsigned int tot_ticks;
    350 	unsigned long rd_ios;
    351 	unsigned long wr_ios;
    352 	unsigned long rd_sec;
    353 	unsigned long wr_sec;
    354 };
    355 
    356 static struct cpu_disk_util_stats before;
    357 static struct cpu_disk_util_stats after;
    358 
    359 #define BUFSIZE		8192
    360 
    361 static int hz;
    362 
    363 static void
    364 get_HZ(void)
    365 {
    366 	if ((hz = sysconf(_SC_CLK_TCK)) == -1)
    367 		exit(1);
    368 }
    369 
    370 #if 0
    371 static int num_cores;
    372 
    373 static void
    374 get_cores(void)
    375 {
    376 	if ((num_cores = sysconf(_SC_NPROCESSORS_ONLN)) == -1)
    377 		exit(1);
    378 }
    379 #endif
    380 
    381 extern char *blockdev_name;
    382 
    383 static void
    384 get_blockdev_name(char *bdev)
    385 {
    386 	char dev_name[BUFSIZE];
    387 	FILE *cmd;
    388 
    389 	cmd = popen("getprop ro.product.name", "r");
    390 	if (cmd == NULL) {
    391 		fprintf(stderr, "%s: Cannot popen getprop\n",
    392 			progname);
    393 		exit(1);
    394 	}
    395 	if (fgets(dev_name, BUFSIZE, cmd) == NULL) {
    396 		fprintf(stderr,
    397 			"%s: Bad output from getprop ro.product.name\n",
    398 			progname);
    399 		exit(1);
    400 	}
    401 	pclose(cmd);
    402 	/* strncmp needed because of the trailing '\n' */
    403 	if (strncmp(dev_name, "bullhead", strlen("bullhead")) == 0 ||
    404 	    strncmp(dev_name, "angler", strlen("angler")) == 0 ||
    405 	    strncmp(dev_name, "shamu", strlen("shamu")) == 0 ||
    406 	    strncmp(dev_name, "aosp_gobo", strlen("aosp_gobo")) == 0 ||
    407 	    strncmp(dev_name, "full_k37_y33_gms", strlen("full_k37_y33_gms")) == 0 ||
    408 	    strncmp(dev_name, "fugu", strlen("fugu")) == 0) {
    409 		strcpy(bdev, "mmcblk0");
    410 	} else if (strncmp(dev_name, "marlin", strlen("marlin")) == 0 ||
    411 		   strncmp(dev_name, "sailfish", strlen("sailfish")) == 0 ||
    412 		   strncmp(dev_name, "taimen", strlen("taimen")) == 0 ||
    413 		   strncmp(dev_name, "walleye", strlen("walleye")) == 0) {
    414 		strcpy(bdev, "sda");
    415 	} else if (blockdev_name != NULL) {
    416 		strcpy(bdev, blockdev_name);
    417 	} else {
    418 		fprintf(stderr,
    419 			"%s: Unknown device %s, please specify block device name with -b\n",
    420 			progname, dev_name);
    421 		exit(1);
    422 	}
    423 }
    424 
    425 static void
    426 read_disk_util_state(struct cpu_disk_util_stats *state)
    427 {
    428 	FILE *fp;
    429         char line[BUFSIZE], dev_name[BUFSIZE];
    430         unsigned int major, minor;
    431 	unsigned int ios_pgr;
    432 	unsigned int rq_ticks;
    433 	unsigned int wr_ticks;
    434 	unsigned long rd_ticks;
    435 	unsigned long rd_merges;
    436 	unsigned long wr_merges;
    437 	unsigned long up_sec, up_cent;
    438 	char blockdev_name[BUFSIZE];
    439 
    440 	/* Read and parse /proc/uptime */
    441 	fp = fopen("/proc/uptime", "r");
    442 	if (fgets(line, sizeof(line), fp) == NULL) {
    443 		fprintf(stderr, "%s: Cannot read /proc/uptime\n",
    444 			progname);
    445 		exit(1);
    446 	}
    447 	fclose(fp);
    448 	sscanf(line, "%lu.%lu", &up_sec, &up_cent);
    449 	state->uptime = (unsigned long long) up_sec * hz +
    450 		(unsigned long long) up_cent * hz / 100;
    451 
    452 	/* Read and parse /proc/diskstats */
    453 	get_blockdev_name(blockdev_name);
    454 	fp = fopen("/proc/diskstats", "r");
    455 	while (fgets(line, sizeof(line), fp)) {
    456 		sscanf(line,
    457 		       "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u",
    458 		       &major, &minor, dev_name,
    459 		       &state->rd_ios, &rd_merges, &state->rd_sec,
    460 		       &rd_ticks, &state->wr_ios, &wr_merges,
    461 		       &state->wr_sec, &wr_ticks,
    462 		       &ios_pgr, &state->tot_ticks, &rq_ticks);
    463                 if (strcmp(dev_name, blockdev_name) == 0) {
    464 			/*
    465 			 * tot_ticks is "number of milliseconds spent
    466 			 * doing I/Os". Look at Documentation/iostats.txt.
    467 			 * Or at genhd.c:diskstats_show(), which calls
    468 			 * jiffies_to_msecs() on this field before printing
    469 			 * it. Convert this to hz, so we can do all our math
    470 			 * in ticks.
    471 			 */
    472 			state->tot_ticks /= 1000; /* to seconds */
    473 			state->tot_ticks *= hz;   /* to hz	*/
    474 			fclose(fp);
    475 			return;
    476 		}
    477 	}
    478         fprintf(stderr, "%s: Did not find device sda in /proc/diskstats\n",
    479 		progname);
    480 	exit(1);
    481 }
    482 
    483 static void
    484 read_cpu_util_state(struct cpu_disk_util_stats *state)
    485 {
    486 	FILE *fp;
    487 	char line[BUFSIZE], cpu[BUFSIZE];
    488 
    489 	/* Read and parse /proc/stat */
    490 	fp = fopen("/proc/stat", "r");
    491 	if (fgets(line, sizeof(line), fp) == NULL) {
    492 		fprintf(stderr, "%s: Cannot read /proc/stat\n",
    493 			progname);
    494 		exit(1);
    495 	}
    496 	fclose(fp);
    497 	sscanf(line, "%s %ju %ju %ju %ju %ju %ju %ju",
    498 	       cpu,
    499 	       &state->user_cpu_ticks,
    500 	       &state->nice_cpu_ticks,
    501 	       &state->system_cpu_ticks,
    502 	       &state->idle_cpu_ticks,
    503 	       &state->iowait_cpu_ticks,
    504 	       &state->hardirq_cpu_ticks,
    505 	       &state->softirq_cpu_ticks);
    506 }
    507 
    508 void
    509 capture_util_state_before(void)
    510 {
    511 	get_HZ();
    512 	read_disk_util_state(&before);
    513 	read_cpu_util_state(&before);
    514 }
    515 
    516 void
    517 report_cpu_disk_util(void)
    518 {
    519         double disk_util, cpu_util;
    520 	u_int64_t tot1, tot2, delta1, delta2;
    521 
    522 	read_disk_util_state(&after);
    523 	read_cpu_util_state(&after);
    524 	/* CPU Util */
    525 	tot2 = after.user_cpu_ticks + after.nice_cpu_ticks +
    526 		after.system_cpu_ticks + after.hardirq_cpu_ticks +
    527 		after.softirq_cpu_ticks;
    528 	tot1 = before.user_cpu_ticks + before.nice_cpu_ticks +
    529 		before.system_cpu_ticks + before.hardirq_cpu_ticks +
    530 		before.softirq_cpu_ticks;
    531 	delta1 = tot2 - tot1;
    532 	tot2 += after.iowait_cpu_ticks + after.idle_cpu_ticks;
    533 	tot1 += before.iowait_cpu_ticks + before.idle_cpu_ticks;
    534 	delta2 = tot2 - tot1;
    535 	cpu_util = delta1 * 100.0 / delta2;
    536 	if (!summary_mode)
    537 		printf("CPU util = %.2f%%\n", cpu_util);
    538 	else
    539 		printf("%.2f ", cpu_util);
    540 	/* Next compute system (incl irq/softirq) and user cpu util */
    541 	delta1 = (after.user_cpu_ticks + after.nice_cpu_ticks) -
    542 		(before.user_cpu_ticks + before.nice_cpu_ticks);
    543 	cpu_util = delta1 * 100.0 / delta2;
    544 	if (!summary_mode)
    545 		printf("User CPU util = %.2f%%\n", cpu_util);
    546 	else
    547 		printf("%.2f ", cpu_util);
    548 	delta1 = (after.system_cpu_ticks + after.hardirq_cpu_ticks +
    549 		  after.softirq_cpu_ticks) -
    550 		(before.system_cpu_ticks + before.hardirq_cpu_ticks +
    551 		 before.softirq_cpu_ticks);
    552 	cpu_util = delta1 * 100.0 / delta2;
    553 	if (!summary_mode)
    554 		printf("System CPU util = %.2f%%\n", cpu_util);
    555 	else
    556 		printf("%.2f ", cpu_util);
    557 	/* Disk Util */
    558 	disk_util = (after.tot_ticks - before.tot_ticks) * 100.0 /
    559 		(after.uptime - before.uptime);
    560 	if (verbose) {
    561 		printf("Reads : nr_ios %lu, MB read %lu\n",
    562 	       (after.rd_ios - before.rd_ios),
    563 	       (after.rd_sec - before.rd_sec) / 2048);
    564 		printf("Writes : nr_ios %lu, MB written %lu\n",
    565 	       (after.wr_ios - before.wr_ios),
    566 		       (after.wr_sec - before.wr_sec) / 2048);
    567 	}
    568 	if (!summary_mode)
    569 		printf("Disk util = %.2f%%\n", disk_util);
    570 	else
    571 		printf("%.2f", disk_util);
    572 }
    573 
    574 
    575 static struct ioshark_filename_struct *filename_cache;
    576 static int filename_cache_num_entries;
    577 
    578 char *
    579 get_ro_filename(int ix)
    580 {
    581 	if (ix >= filename_cache_num_entries)
    582 		return NULL;
    583 	return filename_cache[ix].path;
    584 }
    585 
    586 void
    587 init_filename_cache(void)
    588 {
    589 	int fd;
    590 	struct stat st;
    591 
    592 	fd = open("ioshark_filenames", O_RDONLY);
    593 	if (fd < 0) {
    594 		fprintf(stderr, "%s Can't open ioshark_filenames file\n",
    595 			progname);
    596 		exit(EXIT_FAILURE);
    597 	}
    598 	if (fstat(fd, &st) < 0) {
    599 		fprintf(stderr, "%s Can't fstat ioshark_filenames file\n",
    600 			progname);
    601 		exit(EXIT_FAILURE);
    602 	}
    603 	filename_cache_num_entries = st.st_size /
    604 		sizeof(struct ioshark_filename_struct);
    605 	filename_cache = mmap(NULL, st.st_size, PROT_READ,
    606 			      MAP_SHARED | MAP_LOCKED | MAP_POPULATE,
    607 			      fd, 0);
    608 	if (filename_cache == MAP_FAILED) {
    609 		fprintf(stderr, "%s Can't fstat ioshark_filenames file: %s\n",
    610 			progname, strerror(errno));
    611 		exit(EXIT_FAILURE);
    612 	}
    613 	close(fd);
    614 }
    615 
    616 void
    617 free_filename_cache(void)
    618 {
    619 	size_t mmap_size;
    620 
    621 	mmap_size = filename_cache_num_entries *
    622 		sizeof(struct ioshark_filename_struct);
    623 	munmap(filename_cache, mmap_size);
    624 }
    625 
    626 /*
    627  * Is the passed in filename a regular file ? (eg. not a directory).
    628  * Second, is it in a read-only partition ?
    629  */
    630 int
    631 is_readonly_mount(char *filename, size_t size)
    632 {
    633 	struct statfs statfsbuf;
    634 	struct stat statbuf;
    635 
    636 	if (stat(filename, &statbuf) < 0) {
    637 		/* File possibly deleted */
    638 		return 0;
    639 	}
    640 	if (!S_ISREG(statbuf.st_mode)) {
    641 		/* Is it a regular file ? */
    642 		return 0;
    643 	}
    644 	if ((size_t)statbuf.st_size < size) {
    645 		/* Size of existing file is smaller than we expect */
    646 		return 0;
    647 	}
    648 	if (statfs(filename, &statfsbuf) < 0) {
    649 		/* This shouldn't happen */
    650 		return 0;
    651 	}
    652 	if ((statfsbuf.f_flags & ST_RDONLY) == 0)
    653 		return 0;
    654 	else
    655 		return 1;
    656 }
    657 
    658 int
    659 ioshark_read_header(FILE *fp, struct ioshark_header *header)
    660 {
    661 	if (fread(header, sizeof(struct ioshark_header), 1, fp) != 1)
    662 		return -1;
    663 	header->version = be64toh(header->version);
    664 	header->num_files = be64toh(header->num_files);
    665 	header->num_io_operations = be64toh(header->num_io_operations);
    666 	return 1;
    667 }
    668 
    669 int
    670 ioshark_read_file_state(FILE *fp, struct ioshark_file_state *state)
    671 {
    672 	if (fread(state, sizeof(struct ioshark_file_state), 1, fp) != 1)
    673 		return -1;
    674 	state->fileno = be64toh(state->fileno);
    675 	state->size = be64toh(state->size);
    676 	state->global_filename_ix = be64toh(state->global_filename_ix);
    677 	return 1;
    678 }
    679 
    680 int
    681 ioshark_read_file_op(FILE *fp, struct ioshark_file_operation *file_op)
    682 {
    683 	if (fread(file_op, sizeof(struct ioshark_file_operation), 1, fp) != 1)
    684 		return -1;
    685 	file_op->delta_us = be64toh(file_op->delta_us);
    686 	file_op->op_union.enum_size = be32toh(file_op->op_union.enum_size);
    687 	file_op->fileno = be64toh(file_op->fileno);
    688 	switch (file_op->ioshark_io_op) {
    689 	case IOSHARK_LSEEK:
    690 	case IOSHARK_LLSEEK:
    691 		file_op->lseek_offset = be64toh(file_op->lseek_offset);
    692 		file_op->lseek_action = be32toh(file_op->lseek_action);
    693 		break;
    694 	case IOSHARK_PREAD64:
    695 	case IOSHARK_PWRITE64:
    696 		file_op->prw_offset = be64toh(file_op->prw_offset);
    697 		file_op->prw_len = be64toh(file_op->prw_len);
    698 		break;
    699 	case IOSHARK_READ:
    700 	case IOSHARK_WRITE:
    701 		file_op->rw_len = be64toh(file_op->rw_len);
    702 		break;
    703 	case IOSHARK_MMAP:
    704 	case IOSHARK_MMAP2:
    705 		file_op->mmap_offset = be64toh(file_op->mmap_offset);
    706 		file_op->mmap_len = be64toh(file_op->mmap_len);
    707 		file_op->mmap_prot = be32toh(file_op->mmap_prot);
    708 		break;
    709 	case IOSHARK_OPEN:
    710 		file_op->open_flags = be32toh(file_op->open_flags);
    711 		file_op->open_mode = be32toh(file_op->open_mode);
    712 		break;
    713 	case IOSHARK_FSYNC:
    714 	case IOSHARK_FDATASYNC:
    715 		break;
    716 	case IOSHARK_CLOSE:
    717 		break;
    718 	default:
    719 		fprintf(stderr, "%s: unknown FILE_OP %d\n",
    720 			progname, file_op->ioshark_io_op);
    721 		exit(EXIT_FAILURE);
    722 		break;
    723 	}
    724 	return 1;
    725 }
    726