Home | History | Annotate | Download | only in opjitconv
      1 /**
      2  * @file opjitconv.c
      3  * Convert a jit dump file to an ELF file
      4  *
      5  * @remark Copyright 2007 OProfile authors
      6  * @remark Read the file COPYING
      7  *
      8  * @author Jens Wilke
      9  * @Modifications Maynard Johnson
     10  * @Modifications Daniel Hansel
     11  * @Modifications Gisle Dankel
     12  *
     13  * Copyright IBM Corporation 2007
     14  *
     15  */
     16 
     17 #include "opjitconv.h"
     18 #include "opd_printf.h"
     19 #include "op_file.h"
     20 #include "op_libiberty.h"
     21 
     22 #include <dirent.h>
     23 #include <fnmatch.h>
     24 #include <errno.h>
     25 #include <fcntl.h>
     26 #include <limits.h>
     27 #include <pwd.h>
     28 #include <stdint.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <string.h>
     32 #include <sys/mman.h>
     33 #include <sys/types.h>
     34 #include <unistd.h>
     35 #include <wait.h>
     36 
     37 /*
     38  * list head.  The linked list is used during parsing (parse_all) to
     39  * hold all jitentry elements. After parsing, the program works on the
     40  * array structures (entries_symbols_ascending, entries_address_ascending)
     41  * and the linked list is not used any more.
     42  */
     43 struct jitentry * jitentry_list = NULL;
     44 struct jitentry_debug_line * jitentry_debug_line_list = NULL;
     45 
     46 /* Global variable for asymbols so we can free the storage later. */
     47 asymbol ** syms;
     48 
     49 /* jit dump header information */
     50 enum bfd_architecture dump_bfd_arch;
     51 int dump_bfd_mach;
     52 char const * dump_bfd_target_name;
     53 
     54 /* user information for special user 'oprofile' */
     55 struct passwd * pw_oprofile;
     56 
     57 char sys_cmd_buffer[PATH_MAX + 1];
     58 
     59 /* the bfd handle of the ELF file we write */
     60 bfd * cur_bfd;
     61 
     62 /* count of jitentries in the list */
     63 u32 entry_count;
     64 /* maximul space in the entry arrays, needed to add entries */
     65 u32 max_entry_count;
     66 /* array pointing to all jit entries, sorted by symbol names */
     67 struct jitentry ** entries_symbols_ascending;
     68 /* array pointing to all jit entries sorted by address */
     69 struct jitentry ** entries_address_ascending;
     70 
     71 /* debug flag, print some information */
     72 int debug;
     73 
     74 /*
     75  *  Front-end processing from this point to end of the source.
     76  *    From main(), the general flow is as follows:
     77  *      1. Find all anonymous samples directories
     78  *      2. Find all JIT dump files
     79  *      3. For each JIT dump file:
     80  *        3.1 Find matching anon samples dir (from list retrieved in step 1)
     81  *        3.2 mmap the JIT dump file
     82  *        3.3 Call op_jit_convert to create ELF file if necessary
     83  */
     84 
     85 /* Callback function used for get_matching_pathnames() call to obtain
     86  * matching path names.
     87  */
     88 static void get_pathname(char const * pathname, void * name_list)
     89 {
     90 	struct list_head * names = (struct list_head *) name_list;
     91 	struct pathname * pn = xmalloc(sizeof(struct pathname));
     92 	pn->name = xstrdup(pathname);
     93 	list_add(&pn->neighbor, names);
     94 }
     95 
     96 static void delete_pathname(struct pathname * pname)
     97 {
     98 	free(pname->name);
     99 	list_del(&pname->neighbor);
    100 	free(pname);
    101 }
    102 
    103 
    104 static void delete_path_names_list(struct list_head * list)
    105 {
    106 	struct list_head * pos1, * pos2;
    107 	list_for_each_safe(pos1, pos2, list) {
    108 		struct pathname * pname = list_entry(pos1, struct pathname,
    109 						     neighbor);
    110 		delete_pathname(pname);
    111 	}
    112 }
    113 
    114 static int mmap_jitdump(char const * dumpfile,
    115 	struct op_jitdump_info * file_info)
    116 {
    117 	int rc = OP_JIT_CONV_OK;
    118 	int dumpfd;
    119 
    120 	dumpfd = open(dumpfile, O_RDONLY);
    121 	if (dumpfd < 0) {
    122 		if (errno == ENOENT)
    123 			rc = OP_JIT_CONV_NO_DUMPFILE;
    124 		else
    125 			rc = OP_JIT_CONV_FAIL;
    126 		goto out;
    127 	}
    128 	rc = fstat(dumpfd, &file_info->dmp_file_stat);
    129 	if (rc < 0) {
    130 		perror("opjitconv:fstat on dumpfile");
    131 		rc = OP_JIT_CONV_FAIL;
    132 		goto out;
    133 	}
    134 	file_info->dmp_file = mmap(0, file_info->dmp_file_stat.st_size,
    135 				   PROT_READ, MAP_PRIVATE, dumpfd, 0);
    136 	if (file_info->dmp_file == MAP_FAILED) {
    137 		perror("opjitconv:mmap\n");
    138 		rc = OP_JIT_CONV_FAIL;
    139 	}
    140 out:
    141 	return rc;
    142 }
    143 
    144 static char const * find_anon_dir_match(struct list_head * anon_dirs,
    145 					char const * proc_id)
    146 {
    147 	struct list_head * pos;
    148 	char match_filter[10];
    149 	snprintf(match_filter, 10, "*/%s.*", proc_id);
    150 	list_for_each(pos, anon_dirs) {
    151 		struct pathname * anon_dir =
    152 			list_entry(pos, struct pathname, neighbor);
    153 		if (!fnmatch(match_filter, anon_dir->name, 0))
    154 			return anon_dir->name;
    155 	}
    156 	return NULL;
    157 }
    158 
    159 int change_owner(char * path)
    160 {
    161 	int rc = OP_JIT_CONV_OK;
    162 	int fd;
    163 
    164 	fd = open(path, 0);
    165 	if (fd < 0) {
    166 		printf("opjitconv: File cannot be opened for changing ownership.\n");
    167 		rc = OP_JIT_CONV_FAIL;
    168 		goto out;
    169 	}
    170 	if (fchown(fd, pw_oprofile->pw_uid, pw_oprofile->pw_gid) != 0) {
    171 		printf("opjitconv: Changing ownership failed (%s).\n", strerror(errno));
    172 		close(fd);
    173 		rc = OP_JIT_CONV_FAIL;
    174 		goto out;
    175 	}
    176 	close(fd);
    177 
    178 out:
    179 	return rc;
    180 }
    181 
    182 /* Copies the given file to the temporary working directory and sets ownership
    183  * to 'oprofile:oprofile'.
    184  */
    185 int copy_dumpfile(char const * dumpfile, char * tmp_dumpfile)
    186 {
    187 	int rc = OP_JIT_CONV_OK;
    188 
    189 	sprintf(sys_cmd_buffer, "/bin/cp -p %s %s", dumpfile, tmp_dumpfile);
    190 
    191 	if (system(sys_cmd_buffer) != 0) {
    192 		printf("opjitconv: Calling system() to copy files failed.\n");
    193 		rc = OP_JIT_CONV_FAIL;
    194 		goto out;
    195 	}
    196 
    197 	if (change_owner(tmp_dumpfile) != 0) {
    198 		printf("opjitconv: Changing ownership of temporary dump file failed.\n");
    199 		rc = OP_JIT_CONV_FAIL;
    200 		goto out;
    201 	}
    202 
    203 out:
    204 	return rc;
    205 }
    206 
    207 /* Copies the created ELF file located in the temporary working directory to the
    208  * final destination (i.e. given ELF file name) and sets ownership to the
    209  * current user.
    210  */
    211 int copy_elffile(char * elf_file, char * tmp_elffile)
    212 {
    213 	int rc = OP_JIT_CONV_OK;
    214 	int fd;
    215 
    216 	sprintf(sys_cmd_buffer, "/bin/cp -p %s %s", tmp_elffile, elf_file);
    217 	if (system(sys_cmd_buffer) != 0) {
    218 		printf("opjitconv: Calling system() to copy files failed.\n");
    219 		rc = OP_JIT_CONV_FAIL;
    220 		goto out;
    221 	}
    222 
    223 	fd = open(elf_file, 0);
    224 	if (fd < 0) {
    225 		printf("opjitconv: File cannot be opened for changing ownership.\n");
    226 		rc = OP_JIT_CONV_FAIL;
    227 		goto out;
    228 	}
    229 	if (fchown(fd, getuid(), getgid()) != 0) {
    230 		printf("opjitconv: Changing ownership failed (%s).\n", strerror(errno));
    231 		close(fd);
    232 		rc = OP_JIT_CONV_FAIL;
    233 		goto out;
    234 	}
    235 	close(fd);
    236 
    237 out:
    238 	return rc;
    239 }
    240 
    241 /* Look for an anonymous samples directory that matches the process ID
    242  * given by the passed JIT dmp_pathname.  If none is found, it's an error
    243  * since by agreement, all JIT dump files should be removed every time
    244  * the user does --reset.  If we do find the matching samples directory,
    245  * we create an ELF file (<proc_id>.jo) and place it in that directory.
    246  */
    247 static int process_jit_dumpfile(char const * dmp_pathname,
    248 				struct list_head * anon_sample_dirs,
    249 				unsigned long long start_time,
    250 				unsigned long long end_time,
    251 				char * tmp_conv_dir)
    252 {
    253 	int result_dir_length, proc_id_length;
    254 	int rc = OP_JIT_CONV_OK;
    255 	int jofd;
    256 	struct stat file_stat;
    257 	time_t dumpfile_modtime;
    258 	struct op_jitdump_info dmp_info;
    259 	char * elf_file = NULL;
    260 	char * proc_id = NULL;
    261 	char const * anon_dir;
    262 	char const * dumpfilename = rindex(dmp_pathname, '/');
    263 	/* temporary copy of dump file created for conversion step */
    264 	char * tmp_dumpfile;
    265 	/* temporary ELF file created during conversion step */
    266 	char * tmp_elffile;
    267 
    268 	verbprintf(debug, "Processing dumpfile %s\n", dmp_pathname);
    269 
    270 	/* Check if the dump file is a symbolic link.
    271 	 * We should not trust symbolic links because we only produce normal dump
    272 	 * files (no links).
    273 	 */
    274 	if (lstat(dmp_pathname, &file_stat) == -1) {
    275 		printf("opjitconv: lstat for dumpfile failed (%s).\n", strerror(errno));
    276 		rc = OP_JIT_CONV_FAIL;
    277 		goto out;
    278 	}
    279 	if (S_ISLNK(file_stat.st_mode)) {
    280 		printf("opjitconv: dumpfile path is corrupt (symbolic links not allowed).\n");
    281 		rc = OP_JIT_CONV_FAIL;
    282 		goto out;
    283 	}
    284 
    285 	if (dumpfilename) {
    286 		size_t tmp_conv_dir_length = strlen(tmp_conv_dir);
    287 		char const * dot_dump = rindex(++dumpfilename, '.');
    288 		if (!dot_dump)
    289 			goto chk_proc_id;
    290 		proc_id_length = dot_dump - dumpfilename;
    291 		proc_id = xmalloc(proc_id_length + 1);
    292 		memcpy(proc_id, dumpfilename, proc_id_length);
    293 		proc_id[proc_id_length] = '\0';
    294 		verbprintf(debug, "Found JIT dumpfile for process %s\n",
    295 			   proc_id);
    296 
    297 		tmp_dumpfile = xmalloc(tmp_conv_dir_length + 1 + strlen(dumpfilename) + 1);
    298 		strncpy(tmp_dumpfile, tmp_conv_dir, tmp_conv_dir_length);
    299 		tmp_dumpfile[tmp_conv_dir_length] = '\0';
    300 		strcat(tmp_dumpfile, "/");
    301 		strcat(tmp_dumpfile, dumpfilename);
    302 	}
    303 chk_proc_id:
    304 	if (!proc_id) {
    305 		printf("opjitconv: dumpfile path is corrupt.\n");
    306 		rc = OP_JIT_CONV_FAIL;
    307 		goto out;
    308 	}
    309 	if (!(anon_dir = find_anon_dir_match(anon_sample_dirs, proc_id))) {
    310 		printf("Possible error: No matching anon samples for %s\n",
    311 		       dmp_pathname);
    312 		rc = OP_JIT_CONV_NO_MATCHING_ANON_SAMPLES;
    313 		goto free_res1;
    314 	}
    315 
    316 	if (copy_dumpfile(dmp_pathname, tmp_dumpfile) != OP_JIT_CONV_OK)
    317 		goto free_res1;
    318 
    319 	if ((rc = mmap_jitdump(tmp_dumpfile, &dmp_info)) == OP_JIT_CONV_OK) {
    320 		char * anon_path_seg = rindex(anon_dir, '/');
    321 		if (!anon_path_seg) {
    322 			printf("opjitconv: Bad path for anon sample: %s\n",
    323 			       anon_dir);
    324 			rc = OP_JIT_CONV_FAIL;
    325 			goto free_res2;
    326 		}
    327 		result_dir_length = ++anon_path_seg - anon_dir;
    328 		/* create final ELF file name */
    329 		elf_file = xmalloc(result_dir_length +
    330 				   strlen(proc_id) + strlen(".jo") + 1);
    331 		strncpy(elf_file, anon_dir, result_dir_length);
    332 		elf_file[result_dir_length] = '\0';
    333 		strcat(elf_file, proc_id);
    334 		strcat(elf_file, ".jo");
    335 		/* create temporary ELF file name */
    336 		tmp_elffile = xmalloc(strlen(tmp_conv_dir) + 1 +
    337 				   strlen(proc_id) + strlen(".jo") + 1);
    338 		strncpy(tmp_elffile, tmp_conv_dir, strlen(tmp_conv_dir));
    339 		tmp_elffile[strlen(tmp_conv_dir)] = '\0';
    340 		strcat(tmp_elffile, "/");
    341 		strcat(tmp_elffile, proc_id);
    342 		strcat(tmp_elffile, ".jo");
    343 
    344 		// Check if final ELF file exists already
    345 		jofd = open(elf_file, O_RDONLY);
    346 		if (jofd < 0)
    347 			goto create_elf;
    348 		rc = fstat(jofd, &file_stat);
    349 		if (rc < 0) {
    350 			perror("opjitconv:fstat on .jo file");
    351 			rc = OP_JIT_CONV_FAIL;
    352 			goto free_res3;
    353 		}
    354 		if (dmp_info.dmp_file_stat.st_mtime >
    355 		    dmp_info.dmp_file_stat.st_ctime)
    356 			dumpfile_modtime = dmp_info.dmp_file_stat.st_mtime;
    357 		else
    358 			dumpfile_modtime = dmp_info.dmp_file_stat.st_ctime;
    359 
    360 		/* Final ELF file already exists, so if dumpfile has not been
    361 		 * modified since the ELF file's mod time, we don't need to
    362 		 * do ELF creation again.
    363 		 */
    364 		if (!(file_stat.st_ctime < dumpfile_modtime ||
    365 		    file_stat.st_mtime < dumpfile_modtime)) {
    366 			rc = OP_JIT_CONV_ALREADY_DONE;
    367 			goto free_res3;
    368 		}
    369 
    370 	create_elf:
    371 		verbprintf(debug, "Converting %s to %s\n", dmp_pathname,
    372 			   elf_file);
    373 		/* Set eGID of the special user 'oprofile'. */
    374 		if (setegid(pw_oprofile->pw_gid) != 0) {
    375 			perror("opjitconv: setegid to special user failed");
    376 			rc = OP_JIT_CONV_FAIL;
    377 			goto free_res3;
    378 		}
    379 		/* Set eUID of the special user 'oprofile'. */
    380 		if (seteuid(pw_oprofile->pw_uid) != 0) {
    381 			perror("opjitconv: seteuid to special user failed");
    382 			rc = OP_JIT_CONV_FAIL;
    383 			goto free_res3;
    384 		}
    385 		/* Convert the dump file as the special user 'oprofile'. */
    386 		rc = op_jit_convert(dmp_info, tmp_elffile, start_time, end_time);
    387 		/* Set eUID back to the original user. */
    388 		if (seteuid(getuid()) != 0) {
    389 			perror("opjitconv: seteuid to original user failed");
    390 			rc = OP_JIT_CONV_FAIL;
    391 			goto free_res3;
    392 		}
    393 		/* Set eGID back to the original user. */
    394 		if (setegid(getgid()) != 0) {
    395 			perror("opjitconv: setegid to original user failed");
    396 			rc = OP_JIT_CONV_FAIL;
    397 			goto free_res3;
    398 		}
    399 		rc = copy_elffile(elf_file, tmp_elffile);
    400 	free_res3:
    401 		free(elf_file);
    402 		free(tmp_elffile);
    403 	free_res2:
    404 		munmap(dmp_info.dmp_file, dmp_info.dmp_file_stat.st_size);
    405 	}
    406 free_res1:
    407 	free(proc_id);
    408 	free(tmp_dumpfile);
    409 out:
    410 	return rc;
    411 }
    412 
    413 /* If non-NULL value is returned, caller is responsible for freeing memory.*/
    414 static char * get_procid_from_dirname(char * dirname)
    415 {
    416 	char * ret = NULL;
    417 	if (dirname) {
    418 		char * proc_id;
    419 		int proc_id_length;
    420 		char * fname = rindex(dirname, '/');
    421 		char const * dot = index(++fname, '.');
    422 		if (!dot)
    423 			goto out;
    424 		proc_id_length = dot - fname;
    425 		proc_id = xmalloc(proc_id_length + 1);
    426 		memcpy(proc_id, fname, proc_id_length);
    427 		proc_id[proc_id_length] = '\0';
    428 		ret = proc_id;
    429 	}
    430 out:
    431 	return ret;
    432 }
    433 static void filter_anon_samples_list(struct list_head * anon_dirs)
    434 {
    435 	struct procid {
    436 		struct procid * next;
    437 		char * pid;
    438 	};
    439 	struct procid * pid_list = NULL;
    440 	struct procid * id, * nxt;
    441 	struct list_head * pos1, * pos2;
    442 	list_for_each_safe(pos1, pos2, anon_dirs) {
    443 		struct pathname * pname = list_entry(pos1, struct pathname,
    444 						     neighbor);
    445 		char * proc_id = get_procid_from_dirname(pname->name);
    446 		if (proc_id) {
    447 			int found = 0;
    448 			for (id = pid_list; id != NULL; id = id->next) {
    449 				if (!strcmp(id->pid, proc_id)) {
    450 					/* Already have an entry for this
    451 					 * process ID, so delete this entry
    452 					 * from anon_dirs.
    453 					 */
    454 					free(pname->name);
    455 					list_del(&pname->neighbor);
    456 					free(pname);
    457 					found = 1;
    458 				}
    459 			}
    460 			if (!found) {
    461 				struct procid * this_proc =
    462 					xmalloc(sizeof(struct procid));
    463 				this_proc->pid = proc_id;
    464 				this_proc->next = pid_list;
    465 				pid_list = this_proc;
    466 			}
    467 		} else {
    468 			printf("Unexpected result in processing anon sample"
    469 			       " directory\n");
    470 		}
    471 	}
    472 	for (id = pid_list; id; id = nxt) {
    473 		free(id->pid);
    474 		nxt = id->next;
    475 		free(id);
    476 	}
    477 }
    478 
    479 
    480 static int op_process_jit_dumpfiles(char const * session_dir,
    481 	unsigned long long start_time, unsigned long long end_time)
    482 {
    483 	struct list_head * pos1, * pos2;
    484 	int rc = OP_JIT_CONV_OK;
    485 	char jitdumpfile[PATH_MAX + 1];
    486 	char oprofile_tmp_template[] = "/tmp/oprofile.XXXXXX";
    487 	char const * jitdump_dir = "/var/lib/oprofile/jitdump/";
    488 	LIST_HEAD(jd_fnames);
    489 	char const * anon_dir_filter = "*/{dep}/{anon:anon}/[0-9]*.*";
    490 	LIST_HEAD(anon_dnames);
    491 	char const * samples_subdir = "/samples/current";
    492 	int samples_dir_len = strlen(session_dir) + strlen(samples_subdir);
    493 	char * samples_dir;
    494 	/* temporary working directory for dump file conversion step */
    495 	char * tmp_conv_dir;
    496 
    497 	/* Create a temporary working directory used for the conversion step.
    498 	 */
    499 	tmp_conv_dir = mkdtemp(oprofile_tmp_template);
    500 	if (tmp_conv_dir == NULL) {
    501 		printf("opjitconv: Temporary working directory cannot be created.\n");
    502 		rc = OP_JIT_CONV_FAIL;
    503 		goto out;
    504 	}
    505 
    506 	if ((rc = get_matching_pathnames(&jd_fnames, get_pathname,
    507 		jitdump_dir, "*.dump", NO_RECURSION)) < 0
    508 			|| list_empty(&jd_fnames))
    509 		goto rm_tmp;
    510 
    511 	/* Get user information (i.e. UID and GID) for special user 'oprofile'.
    512 	 */
    513 	pw_oprofile = getpwnam("oprofile");
    514 	if (pw_oprofile == NULL) {
    515 		printf("opjitconv: User information for special user oprofile cannot be found.\n");
    516 		rc = OP_JIT_CONV_FAIL;
    517 		goto rm_tmp;
    518 	}
    519 
    520 	/* Change ownership of the temporary working directory to prevent other users
    521 	 * to attack conversion process.
    522 	 */
    523 	if (change_owner(tmp_conv_dir) != 0) {
    524 		printf("opjitconv: Changing ownership of temporary directory failed.\n");
    525 		rc = OP_JIT_CONV_FAIL;
    526 		goto rm_tmp;
    527 	}
    528 
    529 	samples_dir = xmalloc(samples_dir_len + 1);
    530 	sprintf(samples_dir, "%s%s", session_dir, samples_subdir);
    531 	if (get_matching_pathnames(&anon_dnames, get_pathname,
    532 				    samples_dir, anon_dir_filter,
    533 				    MATCH_DIR_ONLY_RECURSION) < 0
    534 	    || list_empty(&anon_dnames)) {
    535 		rc = OP_JIT_CONV_NO_ANON_SAMPLES;
    536 		goto rm_tmp;
    537 	}
    538 	/* When using get_matching_pathnames to find anon samples,
    539 	 * the list that's returned may contain multiple entries for
    540 	 * one or more processes; e.g.,
    541 	 *    6868.0x100000.0x103000
    542 	 *    6868.0xdfe77000.0xdec40000
    543 	 *    7012.0x100000.0x103000
    544 	 *    7012.0xdfe77000.0xdec40000
    545 	 *
    546 	 * So we must filter the list so there's only one entry per
    547 	 * process.
    548 	 */
    549 	filter_anon_samples_list(&anon_dnames);
    550 
    551 	/* get_matching_pathnames returns only filename segment when
    552 	 * NO_RECURSION is passed, so below, we add back the JIT
    553 	 * dump directory path to the name.
    554 	 */
    555 	list_for_each_safe(pos1, pos2, &jd_fnames) {
    556 		struct pathname * dmpfile =
    557 			list_entry(pos1, struct pathname, neighbor);
    558 		strncpy(jitdumpfile, jitdump_dir, PATH_MAX);
    559 		strncat(jitdumpfile, dmpfile->name, PATH_MAX);
    560 		rc = process_jit_dumpfile(jitdumpfile, &anon_dnames,
    561 					  start_time, end_time, tmp_conv_dir);
    562 		if (rc == OP_JIT_CONV_FAIL) {
    563 			verbprintf(debug, "JIT convert error %d\n", rc);
    564 			goto rm_tmp;
    565 		}
    566 		delete_pathname(dmpfile);
    567 	}
    568 	delete_path_names_list(&anon_dnames);
    569 
    570 rm_tmp:
    571 	/* Delete temporary working directory with all its files
    572 	 * (i.e. dump and ELF file).
    573 	 */
    574 	sprintf(sys_cmd_buffer, "/bin/rm -rf %s", tmp_conv_dir);
    575 	if (system(sys_cmd_buffer) != 0) {
    576 		printf("opjitconv: Removing temporary working directory failed.\n");
    577 		rc = OP_JIT_CONV_TMPDIR_NOT_REMOVED;
    578 	}
    579 
    580 out:
    581 	return rc;
    582 }
    583 
    584 int main(int argc, char ** argv)
    585 {
    586 	unsigned long long start_time, end_time;
    587 	char const * session_dir;
    588 	int rc = 0;
    589 
    590 	debug = 0;
    591 	if (argc > 1 && strcmp(argv[1], "-d") == 0) {
    592 		debug = 1;
    593 		argc--;
    594 		argv++;
    595 	}
    596 
    597 	if (argc != 4) {
    598 		printf("Usage: opjitconv [-d] <session_dir> <starttime>"
    599 		       " <endtime>\n");
    600 		fflush(stdout);
    601 		rc = EXIT_FAILURE;
    602 		goto out;
    603 	}
    604 
    605 	session_dir = argv[1];
    606 	/*
    607 	 * Check for a maximum of 4096 bytes (Linux path name length limit) decremented
    608 	 * by 16 bytes (will be used later for appending samples sub directory).
    609 	 * Integer overflows according to the session dir parameter (user controlled)
    610 	 * are not possible anymore.
    611 	 */
    612 	if (strlen(session_dir) > PATH_MAX - 16) {
    613 		printf("opjitconv: Path name length limit exceeded for session directory: %s\n", session_dir);
    614 		rc = EXIT_FAILURE;
    615 		goto out;
    616 	}
    617 
    618 	start_time = atol(argv[2]);
    619 	end_time = atol(argv[3]);
    620 
    621 	if (start_time > end_time) {
    622 		rc = EXIT_FAILURE;
    623 		goto out;
    624 	}
    625 	verbprintf(debug, "start time/end time is %llu/%llu\n",
    626 		   start_time, end_time);
    627 	rc = op_process_jit_dumpfiles(session_dir, start_time, end_time);
    628 	if (rc > OP_JIT_CONV_OK) {
    629 		verbprintf(debug, "opjitconv: Ending with rc = %d. This code"
    630 			   " is usually OK, but can be useful for debugging"
    631 			   " purposes.\n", rc);
    632 		rc = OP_JIT_CONV_OK;
    633 	}
    634 	fflush(stdout);
    635 	if (rc == OP_JIT_CONV_OK)
    636 		rc = EXIT_SUCCESS;
    637 	else
    638 		rc = EXIT_FAILURE;
    639 out:
    640 	_exit(rc);
    641 }
    642