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