• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
get_pathname(char const * pathname,void * name_list)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 
delete_pathname(struct pathname * pname)96 static void delete_pathname(struct pathname * pname)
97 {
98 	free(pname->name);
99 	list_del(&pname->neighbor);
100 	free(pname);
101 }
102 
103 
delete_path_names_list(struct list_head * list)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 
mmap_jitdump(char const * dumpfile,struct op_jitdump_info * file_info)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 
find_anon_dir_match(struct list_head * anon_dirs,char const * proc_id)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 
change_owner(char * path)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  */
copy_dumpfile(char const * dumpfile,char * tmp_dumpfile)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  */
copy_elffile(char * elf_file,char * tmp_elffile)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  */
process_jit_dumpfile(char const * dmp_pathname,struct list_head * anon_sample_dirs,unsigned long long start_time,unsigned long long end_time,char * tmp_conv_dir)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.*/
get_procid_from_dirname(char * dirname)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 }
filter_anon_samples_list(struct list_head * anon_dirs)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 
op_process_jit_dumpfiles(char const * session_dir,unsigned long long start_time,unsigned long long end_time)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 
main(int argc,char ** argv)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