memzap

replay memory writes
git clone git://git.2f30.org/memzap
Log | Files | Refs | README | LICENSE

memzap.c (5578B)


      1 /* See LICENSE file for copyright and license details. */
      2 
      3 #include <libelf/libelf.h>
      4 #include <libelf/gelf.h>
      5 
      6 #include "proto.h"
      7 
      8 static int verbose = 0;
      9 static int fsymbol = 0;
     10 
     11 static void
     12 usage(const char *prog)
     13 {
     14 	fprintf(stderr, "usage: %s [OPTIONS] prog\n", prog);
     15 	fprintf(stderr, "  -s\tSpecify symbol to trace\n");
     16 	fprintf(stderr, "  -r\tSpecify region to trace\n");
     17 	fprintf(stderr, "  -l\tLength of traced region\n");
     18 	fprintf(stderr, "  -v\tEnable verbose output\n");
     19 	fprintf(stderr, "  -V\tVersioning info\n");
     20 	fprintf(stderr, "  -h\tThis help screen\n");
     21 }
     22 
     23 static void
     24 dump_base_image(const char *name, void *buf, size_t len)
     25 {
     26 	ssize_t ret;
     27 	char mem_path[PATH_MAX];
     28 	int fdmem;
     29 
     30 	snprintf(mem_path, sizeof(mem_path),
     31 		 "%s.mem", name);
     32 	fdmem = open(mem_path, O_CREAT | O_RDWR | O_TRUNC, 0644);
     33 	if (fdmem < 0)
     34 		errx(1, "%s: %s", mem_path, strerror(errno));
     35 
     36 	ret = write(fdmem, buf, len);
     37 	if ((size_t)ret != len)
     38 		err(1, "write");
     39 
     40 	close(fdmem);
     41 }
     42 
     43 int
     44 main(int argc, char *argv[])
     45 {
     46 	struct mem_region *mr_old, *mr_new;
     47 	struct mem_region_diff *rdiff;
     48 	struct mdiff_hdr hdr;
     49 	int c;
     50 	int stat;
     51 	pid_t pid;
     52 	unsigned char *buf, *buf_new;
     53 	char *addr = NULL;
     54 	size_t len = 0;
     55 	int fd;
     56 	char *prog = *argv;
     57 	char mdiff_path[PATH_MAX];
     58 	bool once = false;
     59 	uint64_t cycle = 0;
     60 	char *symbol = NULL, *tmp;
     61 	Elf *e;
     62 	Elf32_Word symbol_count, i;
     63 	Elf_Scn *scn = NULL;
     64 	Elf_Data *edata = NULL;
     65 	GElf_Sym sym;
     66 	GElf_Shdr shdr;
     67 
     68 	while ((c = getopt(argc, argv, "hs:l:r:vV")) != -1) {
     69 		switch (c) {
     70 		case 'h':
     71 			usage(prog);
     72 			return 0;
     73 		case 'v':
     74 			verbose = 1;
     75 			break;
     76 		case 'V':
     77 			printf("%s\n", VERSION);
     78 			return 0;
     79 		case 'r':
     80 			addr = (char *)strtoul(optarg, NULL, 16);
     81 			break;
     82 		case 'l':
     83 			len = strtoul(optarg, NULL, 10);
     84 			break;
     85 		case 's':
     86 			fsymbol = 1;
     87 			symbol = strdup(optarg);
     88 			break;
     89 		case '?':
     90 		default:
     91 			return 1;
     92 		}
     93 	}
     94 	argc -= optind;
     95 	argv += optind;
     96 
     97 	if (argc != 1) {
     98 		usage(prog);
     99 		return 1;
    100 	}
    101 
    102 	if (fsymbol) {
    103 		if (addr || len) {
    104 			errx(1, "You can't trace both a symbol and a region/length");
    105 		}
    106 	} else {
    107 		if (!addr || !len) {
    108 			errx(1, "no memory region/len specified");
    109 		}
    110 	}
    111 
    112 	if (fsymbol) {
    113 		if (elf_version(EV_CURRENT) == EV_NONE)
    114 			errx(1, "ELF library initialization failed: %s",
    115 			     elf_errmsg(-1));
    116 
    117 		fd = open(*argv, O_RDONLY);
    118 		if (fd < 0)
    119 			errx(1, "%s: %s\n", *argv, strerror(errno));
    120 
    121 		e = elf_begin(fd, ELF_C_READ, NULL);
    122 		if (!e)
    123 			errx(1, "elf_begin() failed: %s",
    124 			     elf_errmsg(-1));
    125 
    126 		while ((scn = elf_nextscn(e, scn))) {
    127 			gelf_getshdr(scn, &shdr);
    128 			if (shdr.sh_type == SHT_SYMTAB) {
    129 				edata = elf_getdata(scn, edata);
    130 				symbol_count = shdr.sh_size / shdr.sh_entsize;
    131 				for (i = 0; i < symbol_count; i++) {
    132 					gelf_getsym(edata, i, &sym);
    133 					tmp = elf_strptr(e, shdr.sh_link, sym.st_name);
    134 					if (!strcmp(symbol, tmp)) {
    135 						if (verbose > 0) {
    136 							printf("found symbol %s at %p\n",
    137 							       symbol, (void *)sym.st_value);
    138 						}
    139 						addr = (void *)sym.st_value;
    140 						len = (size_t)sym.st_size;
    141 						break;
    142 					}
    143 				}
    144 				if (!addr || !len)
    145 					errx(1, "symbol %s could not be located", symbol);
    146 			}
    147 		}
    148 
    149 		free(symbol);
    150 		elf_end(e);
    151 		close(fd);
    152 	}
    153 
    154 	if (verbose > 0)
    155 		printf("base address: %p, length: %zu\n",
    156 		       addr, len);
    157 
    158 	pid = fork();
    159 	if (pid < 0)
    160 		err(1, "fork");
    161 
    162 	switch (pid) {
    163 	case 0:
    164 		traceme();
    165 		execl(*argv, *argv, (char *)NULL);
    166 		_Exit(1);
    167 	default:
    168 		break;
    169 	}
    170 
    171 	if (verbose > 0)
    172 		printf("mapping buffers into memory\n");
    173 
    174 	buf = map_buf(len);
    175 	buf_new = map_buf(len);
    176 
    177 	if (verbose > 0)
    178 		printf("single stepping child with pid %jd\n",
    179 		       (intmax_t)pid);
    180 
    181 	wait(&stat);
    182 	if (!WIFSTOPPED(stat))
    183 		goto out_mmap;
    184 	single_step(pid);
    185 	wait(&stat);
    186 	if (!WIFSTOPPED(stat))
    187 		goto out_mmap;
    188 	cycle++;
    189 
    190 	/* Create .mdiff file and initialize the header */
    191 	snprintf(mdiff_path, sizeof(mdiff_path),
    192 		 "%s.mdiff", *argv);
    193 	fd = create_mdiff(mdiff_path);
    194 	memset(&hdr, 0, sizeof(hdr));
    195 	strncpy((char *)hdr.magic, "MDIFF", 5);
    196 	hdr.blksize = BLKSIZE;
    197 	hdr.endianness = 0;
    198 	hdr.version = 1;
    199 	hdr.rsize = len;
    200 	mdiff_start_diff(fd);
    201 
    202 	/* Trace the child process and diff the memory regions
    203 	 * per instruction */
    204 	do {
    205 		/* Read in the Nth generation of the traced region */
    206 		readmem(pid, buf, addr, len);
    207 		if (!once) {
    208 			dump_base_image(*argv, buf, len);
    209 			once = true;
    210 		}
    211 
    212 		/* Build a memory region that tracks this buffer */
    213 		mr_old = build_mem_region(buf, len);
    214 		if (!mr_old)
    215 			errx(1, "failed to build memory region\n");
    216 
    217 		/* Single step the child in order to get the next
    218 		 * generation */
    219 		single_step(pid);
    220 		wait(&stat);
    221 		if (!WIFSTOPPED(stat)) {
    222 			free_mem_region(mr_old);
    223 			break;
    224 		}
    225 		cycle++;
    226 
    227 		/* Read in the (N + 1)th generation of the traced
    228 		 * region */
    229 		readmem(pid, buf_new, addr, len);
    230 
    231 		/* Build a memory region that tracks this buffer */
    232 		mr_new = build_mem_region(buf_new, len);
    233 		if (!mr_new)
    234 			errx(1, "failed to build memory region\n");
    235 
    236 		/* Diff the original copy with the updated copy */
    237 		rdiff = diff_mem_region(mr_old, mr_new);
    238 		if (rdiff->nmrdiffs) {
    239 			/* If we've found differences, then apply them on
    240 			 * the Nth generation */
    241 			apply_diff(mr_old, rdiff);
    242 			hdr.nregions += mdiff_append_rdiff(fd, rdiff, cycle);
    243 		}
    244 
    245 		/* Free these and go up for another run */
    246 		free_mem_region(mr_old);
    247 		free_mem_region(mr_new);
    248 		free_mem_region_diff(rdiff);
    249 	} while(1);
    250 
    251 	/* Insert header at the start of the .mdiff file */
    252 	mdiff_insert_hdr(fd, &hdr);
    253 	close_mdiff(fd);
    254 out_mmap:
    255 	unmap_buf(buf, len);
    256 	unmap_buf(buf_new, len);
    257 	return 0;
    258 }