3 * Convert a jit dump file to an ELF file
5 * @remark Copyright 2007 OProfile authors
6 * @remark Read the file COPYING
9 * @Modifications Maynard Johnson
10 * @Modifications Daniel Hansel
11 * @Modifications Gisle Dankel
13 * Copyright IBM Corporation 2007
17 #include "opjitconv.h"
19 #include "op_libiberty.h"
34 #include <sys/types.h>
40 * list head. The linked list is used during parsing (parse_all) to
41 * hold all jitentry elements. After parsing, the program works on the
42 * array structures (entries_symbols_ascending, entries_address_ascending)
43 * and the linked list is not used any more.
45 struct jitentry * jitentry_list = NULL;
46 struct jitentry_debug_line * jitentry_debug_line_list = NULL;
48 /* Global variable for asymbols so we can free the storage later. */
51 /* jit dump header information */
52 enum bfd_architecture dump_bfd_arch;
54 char const * dump_bfd_target_name;
56 /* user information for special user 'oprofile' */
57 struct passwd * pw_oprofile;
59 char sys_cmd_buffer[PATH_MAX + 1];
61 /* the bfd handle of the ELF file we write */
64 /* count of jitentries in the list */
66 /* maximul space in the entry arrays, needed to add entries */
68 /* array pointing to all jit entries, sorted by symbol names */
69 struct jitentry ** entries_symbols_ascending;
70 /* array pointing to all jit entries sorted by address */
71 struct jitentry ** entries_address_ascending;
73 /* debug flag, print some information */
75 /* indicates opjitconv invoked by non-root user via operf */
77 /* indicates we should delete jitdump files owned by the user */
79 /* Session directory where sample data is stored */
82 static struct option long_options [] = {
83 { "session-dir", required_argument, NULL, 's'},
84 { "debug", no_argument, NULL, 'd'},
85 { "delete-jitdumps", no_argument, NULL, 'j'},
86 { "non-root", no_argument, NULL, 'n'},
87 { "help", no_argument, NULL, 'h'},
90 const char * short_options = "s:djnh";
92 LIST_HEAD(jitdump_deletion_candidates);
95 * Front-end processing from this point to end of the source.
96 * From main(), the general flow is as follows:
97 * 1. Find all anonymous samples directories
98 * 2. Find all JIT dump files
99 * 3. For each JIT dump file:
100 * 3.1 Find matching anon samples dir (from list retrieved in step 1)
101 * 3.2 mmap the JIT dump file
102 * 3.3 Call op_jit_convert to create ELF file if necessary
105 /* Callback function used for get_matching_pathnames() call to obtain
106 * matching path names.
108 static void get_pathname(char const * pathname, void * name_list)
110 struct list_head * names = (struct list_head *) name_list;
111 struct pathname * pn = xmalloc(sizeof(struct pathname));
112 pn->name = xstrdup(pathname);
113 list_add(&pn->neighbor, names);
116 static void delete_pathname(struct pathname * pname)
119 list_del(&pname->neighbor);
124 static void delete_path_names_list(struct list_head * list)
126 struct list_head * pos1, * pos2;
127 list_for_each_safe(pos1, pos2, list) {
128 struct pathname * pname = list_entry(pos1, struct pathname,
130 delete_pathname(pname);
134 static int mmap_jitdump(char const * dumpfile,
135 struct op_jitdump_info * file_info)
137 int rc = OP_JIT_CONV_OK;
140 dumpfd = open(dumpfile, O_RDONLY);
143 rc = OP_JIT_CONV_NO_DUMPFILE;
145 rc = OP_JIT_CONV_FAIL;
148 rc = fstat(dumpfd, &file_info->dmp_file_stat);
150 perror("opjitconv:fstat on dumpfile");
151 rc = OP_JIT_CONV_FAIL;
154 file_info->dmp_file = mmap(0, file_info->dmp_file_stat.st_size,
155 PROT_READ, MAP_PRIVATE, dumpfd, 0);
156 if (file_info->dmp_file == MAP_FAILED) {
157 perror("opjitconv:mmap\n");
158 rc = OP_JIT_CONV_FAIL;
166 static char const * find_anon_dir_match(struct list_head * anon_dirs,
167 char const * proc_id)
169 struct list_head * pos;
170 /* Current PID_MAX_LIMIT (as defined in include/linux/threads.h) is
171 * 4 x 4 x 1024 * 1024 (for 64-bit kernels)
172 * So need to have space for 7 chars for proc_id.
174 char match_filter[12];
175 snprintf(match_filter, 12, "*/%s.*", proc_id);
176 list_for_each(pos, anon_dirs) {
177 struct pathname * anon_dir =
178 list_entry(pos, struct pathname, neighbor);
179 if (!fnmatch(match_filter, anon_dir->name, 0))
180 return anon_dir->name;
185 int change_owner(char * path)
187 int rc = OP_JIT_CONV_OK;
194 printf("opjitconv: File cannot be opened for changing ownership.\n");
195 rc = OP_JIT_CONV_FAIL;
198 if (fchown(fd, pw_oprofile->pw_uid, pw_oprofile->pw_gid) != 0) {
199 printf("opjitconv: Changing ownership failed (%s).\n", strerror(errno));
201 rc = OP_JIT_CONV_FAIL;
210 /* Copies the given file to the temporary working directory and sets ownership
211 * to 'oprofile:oprofile'.
213 int copy_dumpfile(char const * dumpfile, char * tmp_dumpfile)
215 #define OP_JITCONV_USECS_TO_WAIT 1000
217 unsigned int usecs_waited = 0;
218 int rc = OP_JIT_CONV_OK;
219 int fd = open(dumpfile, O_RDONLY);
221 perror("opjitconv failed to open JIT dumpfile");
222 return OP_JIT_CONV_FAIL;
225 // Need OS-level file locking here since opagent may still be writing to the file.
226 rc = flock(fd, LOCK_EX | LOCK_NB);
228 if (usecs_waited < OP_JITCONV_USECS_TO_WAIT) {
233 printf("opjitconv: Unable to obtain lock on %s.\n", dumpfile);
234 rc = OP_JIT_CONV_FAIL;
239 sprintf(sys_cmd_buffer, "/bin/cp -p %s %s", dumpfile, tmp_dumpfile);
240 if (system(sys_cmd_buffer) != 0) {
241 printf("opjitconv: Calling system() to copy files failed.\n");
242 rc = OP_JIT_CONV_FAIL;
246 if (change_owner(tmp_dumpfile) != 0) {
247 printf("opjitconv: Changing ownership of temporary dump file failed.\n");
248 rc = OP_JIT_CONV_FAIL;
253 #undef OP_JITCONV_USECS_TO_WAIT
261 /* Copies the created ELF file located in the temporary working directory to the
262 * final destination (i.e. given ELF file name) and sets ownership to the
265 int copy_elffile(char * elf_file, char * tmp_elffile)
267 int rc = OP_JIT_CONV_OK;
270 sprintf(sys_cmd_buffer, "/bin/cp -p %s %s", tmp_elffile, elf_file);
271 if (system(sys_cmd_buffer) != 0) {
272 printf("opjitconv: Calling system() to copy files failed.\n");
273 rc = OP_JIT_CONV_FAIL;
277 fd = open(elf_file, 0);
279 printf("opjitconv: File cannot be opened for changing ownership.\n");
280 rc = OP_JIT_CONV_FAIL;
283 if (fchown(fd, getuid(), getgid()) != 0) {
284 printf("opjitconv: Changing ownership failed (%s).\n", strerror(errno));
286 rc = OP_JIT_CONV_FAIL;
295 /* Look for an anonymous samples directory that matches the process ID
296 * given by the passed JIT dmp_pathname. If none is found, it's an error
297 * since by agreement, all JIT dump files should be removed every time
298 * the user does --reset. If we do find the matching samples directory,
299 * we create an ELF file (<proc_id>.jo) and place it in that directory.
301 static int process_jit_dumpfile(char const * dmp_pathname,
302 struct list_head * anon_sample_dirs,
303 unsigned long long start_time,
304 unsigned long long end_time,
307 int result_dir_length, proc_id_length;
308 int rc = OP_JIT_CONV_OK;
310 struct stat file_stat;
311 time_t dumpfile_modtime;
312 struct op_jitdump_info dmp_info;
313 char * elf_file = NULL;
314 char * proc_id = NULL;
315 char const * anon_dir;
316 char const * dumpfilename = rindex(dmp_pathname, '/');
317 /* temporary copy of dump file created for conversion step */
319 /* temporary ELF file created during conversion step */
322 verbprintf(debug, "Processing dumpfile %s\n", dmp_pathname);
324 /* Check if the dump file is a symbolic link.
325 * We should not trust symbolic links because we only produce normal dump
328 if (lstat(dmp_pathname, &file_stat) == -1) {
329 printf("opjitconv: lstat for dumpfile failed (%s).\n", strerror(errno));
330 rc = OP_JIT_CONV_FAIL;
333 if (S_ISLNK(file_stat.st_mode)) {
334 printf("opjitconv: dumpfile path is corrupt (symbolic links not allowed).\n");
335 rc = OP_JIT_CONV_FAIL;
340 size_t tmp_conv_dir_length = strlen(tmp_conv_dir);
341 char const * dot_dump = rindex(++dumpfilename, '.');
344 proc_id_length = dot_dump - dumpfilename;
345 proc_id = xmalloc(proc_id_length + 1);
346 memcpy(proc_id, dumpfilename, proc_id_length);
347 proc_id[proc_id_length] = '\0';
348 verbprintf(debug, "Found JIT dumpfile for process %s\n",
351 tmp_dumpfile = xmalloc(tmp_conv_dir_length + 1 + strlen(dumpfilename) + 1);
352 strncpy(tmp_dumpfile, tmp_conv_dir, tmp_conv_dir_length);
353 tmp_dumpfile[tmp_conv_dir_length] = '\0';
354 strcat(tmp_dumpfile, "/");
355 strcat(tmp_dumpfile, dumpfilename);
359 printf("opjitconv: dumpfile path is corrupt.\n");
360 rc = OP_JIT_CONV_FAIL;
363 if (!(anon_dir = find_anon_dir_match(anon_sample_dirs, proc_id))) {
364 /* When profiling with operf, opjitconv will remove old jitdump
365 * files (see _cleanup_jitdumps() for details). But this cleanup
366 * strategy makes it quite likely that opjitconv will sometimes find
367 * jitdump files that are not owned by the current user or are in use
368 * by other operf users, thus, the current profile data would not have
369 * matching anon samples for such jitdump files.
371 verbprintf(debug, "Informational message: No matching anon samples for %s\n",
373 rc = OP_JIT_CONV_NO_MATCHING_ANON_SAMPLES;
377 if (copy_dumpfile(dmp_pathname, tmp_dumpfile) != OP_JIT_CONV_OK)
380 if ((rc = mmap_jitdump(tmp_dumpfile, &dmp_info)) == OP_JIT_CONV_OK) {
381 char * anon_path_seg = rindex(anon_dir, '/');
382 if (!anon_path_seg) {
383 printf("opjitconv: Bad path for anon sample: %s\n",
385 rc = OP_JIT_CONV_FAIL;
388 result_dir_length = ++anon_path_seg - anon_dir;
389 /* create final ELF file name */
390 elf_file = xmalloc(result_dir_length +
391 strlen(proc_id) + strlen(".jo") + 1);
392 strncpy(elf_file, anon_dir, result_dir_length);
393 elf_file[result_dir_length] = '\0';
394 strcat(elf_file, proc_id);
395 strcat(elf_file, ".jo");
396 /* create temporary ELF file name */
397 tmp_elffile = xmalloc(strlen(tmp_conv_dir) + 1 +
398 strlen(proc_id) + strlen(".jo") + 1);
399 strncpy(tmp_elffile, tmp_conv_dir, strlen(tmp_conv_dir));
400 tmp_elffile[strlen(tmp_conv_dir)] = '\0';
401 strcat(tmp_elffile, "/");
402 strcat(tmp_elffile, proc_id);
403 strcat(tmp_elffile, ".jo");
405 // Check if final ELF file exists already
406 jofd = open(elf_file, O_RDONLY);
409 rc = fstat(jofd, &file_stat);
412 perror("opjitconv:fstat on .jo file");
413 rc = OP_JIT_CONV_FAIL;
416 if (dmp_info.dmp_file_stat.st_mtime >
417 dmp_info.dmp_file_stat.st_ctime)
418 dumpfile_modtime = dmp_info.dmp_file_stat.st_mtime;
420 dumpfile_modtime = dmp_info.dmp_file_stat.st_ctime;
422 /* Final ELF file already exists, so if dumpfile has not been
423 * modified since the ELF file's mod time, we don't need to
424 * do ELF creation again.
426 if (!(file_stat.st_ctime < dumpfile_modtime ||
427 file_stat.st_mtime < dumpfile_modtime)) {
428 rc = OP_JIT_CONV_ALREADY_DONE;
433 verbprintf(debug, "Converting %s to %s\n", dmp_pathname,
435 /* Set eGID of the special user 'oprofile'. */
436 if (!non_root && setegid(pw_oprofile->pw_gid) != 0) {
437 perror("opjitconv: setegid to special user failed");
438 rc = OP_JIT_CONV_FAIL;
441 /* Set eUID of the special user 'oprofile'. */
442 if (!non_root && seteuid(pw_oprofile->pw_uid) != 0) {
443 perror("opjitconv: seteuid to special user failed");
444 rc = OP_JIT_CONV_FAIL;
447 /* Convert the dump file as the special user 'oprofile'. */
448 rc = op_jit_convert(&dmp_info, tmp_elffile, start_time, end_time);
452 /* Set eUID back to the original user. */
453 if (!non_root && seteuid(getuid()) != 0) {
454 perror("opjitconv: seteuid to original user failed");
455 rc = OP_JIT_CONV_FAIL;
458 /* Set eGID back to the original user. */
459 if (!non_root && setegid(getgid()) != 0) {
460 perror("opjitconv: setegid to original user failed");
461 rc = OP_JIT_CONV_FAIL;
464 rc = copy_elffile(elf_file, tmp_elffile);
469 munmap(dmp_info.dmp_file, dmp_info.dmp_file_stat.st_size);
478 /* If non-NULL value is returned, caller is responsible for freeing memory.*/
479 static char * get_procid_from_dirname(char * dirname)
485 char * fname = rindex(dirname, '/');
486 char const * dot = index(++fname, '.');
489 proc_id_length = dot - fname;
490 proc_id = xmalloc(proc_id_length + 1);
491 memcpy(proc_id, fname, proc_id_length);
492 proc_id[proc_id_length] = '\0';
498 static void filter_anon_samples_list(struct list_head * anon_dirs)
501 struct procid * next;
504 struct procid * pid_list = NULL;
505 struct procid * id, * nxt;
506 struct list_head * pos1, * pos2;
507 list_for_each_safe(pos1, pos2, anon_dirs) {
508 struct pathname * pname = list_entry(pos1, struct pathname,
510 char * proc_id = get_procid_from_dirname(pname->name);
513 for (id = pid_list; id != NULL; id = id->next) {
514 if (!strcmp(id->pid, proc_id)) {
515 /* Already have an entry for this
516 * process ID, so delete this entry
520 list_del(&pname->neighbor);
526 struct procid * this_proc =
527 xmalloc(sizeof(struct procid));
528 this_proc->pid = proc_id;
529 this_proc->next = pid_list;
530 pid_list = this_proc;
533 printf("Unexpected result in processing anon sample"
537 for (id = pid_list; id; id = nxt) {
545 static void _add_jitdumps_to_deletion_list(void * all_jitdumps, char const * jitdump_dir )
547 struct list_head * jd_fnames = (struct list_head *) all_jitdumps;
548 struct list_head * pos1, *pos2;
549 size_t dir_len = strlen(jitdump_dir);
551 list_for_each_safe(pos1, pos2, jd_fnames) {
552 struct pathname * dmpfile =
553 list_entry(pos1, struct pathname, neighbor);
555 char dmpfile_pathname[dir_len + 20];
557 memset(dmpfile_pathname, '\0', dir_len + 20);
558 strcpy(dmpfile_pathname, jitdump_dir);
559 strcat(dmpfile_pathname,dmpfile->name);
560 fd = open(dmpfile_pathname, O_RDONLY);
562 // Non-fatal error, so just display debug message and continue
563 verbprintf(debug, "opjitconv: cannot open jitdump file %s\n",
567 if (fstat(fd, &mystat) < 0) {
568 // Non-fatal error, so just display debug message and continue
569 verbprintf(debug, "opjitconv: cannot fstat jitdump file");
574 if (!non_root || geteuid() == mystat.st_uid) {
575 struct jitdump_deletion_candidate * jdc =
576 xmalloc(sizeof(struct jitdump_deletion_candidate));
577 jdc->name = xstrdup(dmpfile->name);
578 list_add(&jdc->neighbor, &jitdump_deletion_candidates);
583 static int op_process_jit_dumpfiles(char const * session_dir,
584 unsigned long long start_time, unsigned long long end_time)
586 struct list_head * pos1, * pos2;
587 int rc = OP_JIT_CONV_OK;
588 char jitdumpfile[PATH_MAX + 1];
589 char oprofile_tmp_template[PATH_MAX + 1];
590 char const * jitdump_dir = "/tmp/.oprofile/jitdump/";
592 LIST_HEAD(jd_fnames);
593 char const * anon_dir_filter = "*/{dep}/{anon:anon}/[0-9]*.*";
594 LIST_HEAD(anon_dnames);
595 char const * samples_subdir = "/samples/current";
596 int samples_dir_len = strlen(session_dir) + strlen(samples_subdir);
598 /* temporary working directory for dump file conversion step */
599 char * tmp_conv_dir = NULL;
602 sprintf(oprofile_tmp_template, "%s/tmp", session_dir);
604 strcpy(oprofile_tmp_template, "/tmp/oprofile.XXXXXX");
606 /* Create a temporary working directory used for the conversion step.
609 sprintf(sys_cmd_buffer, "/bin/rm -rf %s", oprofile_tmp_template);
610 if (system(sys_cmd_buffer) != 0) {
611 printf("opjitconv: Removing temporary working directory %s failed.\n",
612 oprofile_tmp_template);
613 rc = OP_JIT_CONV_TMPDIR_NOT_REMOVED;
615 if (!mkdir(oprofile_tmp_template, S_IRWXU | S_IRWXG ))
616 tmp_conv_dir = oprofile_tmp_template;
619 tmp_conv_dir = mkdtemp(oprofile_tmp_template);
622 if (tmp_conv_dir == NULL) {
623 printf("opjitconv: Temporary working directory %s cannot be created.\n",
624 oprofile_tmp_template);
625 perror("Exiting due to error");
626 rc = OP_JIT_CONV_FAIL;
632 if ((rc = get_matching_pathnames(&jd_fnames, get_pathname,
633 jitdump_dir, "*.dump", NO_RECURSION)) < 0
634 || list_empty(&jd_fnames)) {
636 if (errno != ENOENT) {
638 strcpy(msg, "opjitconv: fatal error trying to find JIT dump files in ");
639 strcat(msg, jitdump_dir);
641 rc = OP_JIT_CONV_FAIL;
643 verbprintf(debug, "opjitconv: Non-fatal error trying to find JIT dump files in %s: %s\n",
644 jitdump_dir, strerror(errno));
645 rc = OP_JIT_CONV_NO_DUMPFILE;
652 _add_jitdumps_to_deletion_list(&jd_fnames, jitdump_dir);
654 /* Get user information (i.e. UID and GID) for special user 'oprofile'.
659 pw_oprofile = getpwnam("oprofile");
660 if (pw_oprofile == NULL) {
661 printf("opjitconv: User information for special user oprofile cannot be found.\n");
662 rc = OP_JIT_CONV_FAIL;
667 /* Change ownership of the temporary working directory to prevent other users
668 * to attack conversion process.
670 if (change_owner(tmp_conv_dir) != 0) {
671 printf("opjitconv: Changing ownership of temporary directory failed.\n");
672 rc = OP_JIT_CONV_FAIL;
676 samples_dir = xmalloc(samples_dir_len + 1);
677 sprintf(samples_dir, "%s%s", session_dir, samples_subdir);
678 if (get_matching_pathnames(&anon_dnames, get_pathname,
679 samples_dir, anon_dir_filter,
680 MATCH_DIR_ONLY_RECURSION) < 0
681 || list_empty(&anon_dnames)) {
682 rc = OP_JIT_CONV_NO_ANON_SAMPLES;
685 /* When using get_matching_pathnames to find anon samples,
686 * the list that's returned may contain multiple entries for
687 * one or more processes; e.g.,
688 * 6868.0x100000.0x103000
689 * 6868.0xdfe77000.0xdec40000
690 * 7012.0x100000.0x103000
691 * 7012.0xdfe77000.0xdec40000
693 * So we must filter the list so there's only one entry per
696 filter_anon_samples_list(&anon_dnames);
698 /* get_matching_pathnames returns only filename segment when
699 * NO_RECURSION is passed, so below, we add back the JIT
700 * dump directory path to the name.
702 list_for_each_safe(pos1, pos2, &jd_fnames) {
703 struct pathname * dmpfile =
704 list_entry(pos1, struct pathname, neighbor);
705 strncpy(jitdumpfile, jitdump_dir, PATH_MAX);
706 strncat(jitdumpfile, dmpfile->name, PATH_MAX);
707 rc = process_jit_dumpfile(jitdumpfile, &anon_dnames,
708 start_time, end_time, tmp_conv_dir);
709 if (rc == OP_JIT_CONV_FAIL) {
710 verbprintf(debug, "JIT convert error %d\n", rc);
713 delete_pathname(dmpfile);
715 delete_path_names_list(&anon_dnames);
718 /* Delete temporary working directory with all its files
719 * (i.e. dump and ELF file).
721 sprintf(sys_cmd_buffer, "/bin/rm -rf '%s'", tmp_conv_dir);
722 if (system(sys_cmd_buffer) != 0) {
723 printf("opjitconv: Removing temporary working directory failed.\n");
724 rc = OP_JIT_CONV_TMPDIR_NOT_REMOVED;
731 static void _cleanup_jitdumps(void)
733 struct list_head * pos1, *pos2;
734 char const * jitdump_dir = "/tmp/.oprofile/jitdump/";
735 size_t dir_len = strlen(jitdump_dir);
736 char dmpfile_pathname[dir_len + 20];
737 char proc_fd_dir[PATH_MAX];
739 if (!delete_jitdumps)
742 /* The delete_jitdumps flag tells us to try to delete the jitdump files we found
743 * that belong to this user. Only operf should pass the --delete-jitdumps
744 * argument to opjitconv since legacy oprofile uses opcontrol to delete old
747 * The code below will only delete jitdump files that are not currently
748 * being used by another process.
750 list_for_each_safe(pos1, pos2, &jitdump_deletion_candidates) {
752 struct dirent * dirent;
754 size_t dmpfile_name_len;
755 int do_not_delete = 0;
756 struct jitdump_deletion_candidate * cand = list_entry(pos1,
757 struct jitdump_deletion_candidate,
759 memset(dmpfile_pathname, '\0', dir_len + 20);
760 memset(proc_fd_dir, '\0', PATH_MAX);
762 if ((sscanf(cand->name, "%d", &pid)) != 1) {
763 verbprintf(debug, "Cannot get process id from jitdump file %s\n",
768 strcpy(dmpfile_pathname, jitdump_dir);
769 strcat(dmpfile_pathname, cand->name);
770 dmpfile_name_len = strlen(dmpfile_pathname);
772 sprintf(proc_fd_dir, "/proc/%d/fd/", pid);
773 if ((dir = opendir(proc_fd_dir))) {
774 size_t proc_fd_dir_len = strlen(proc_fd_dir);
775 while ((dirent = readdir(dir))) {
776 if (dirent->d_type == DT_LNK) {
779 memset(buf, '\0', 1024);
780 memset(fname, '\0', 1024);
781 memset(buf, '\0', 1024);
782 strcpy(fname, proc_fd_dir);
783 strncat(fname, dirent->d_name, 1023 - proc_fd_dir_len);
784 if (readlink(fname, buf, 1023) > 0) {
785 verbprintf(debug, "readlink found for %s\n", buf);
786 if (strncmp(buf, dmpfile_pathname,
787 dmpfile_name_len) == 0) {
796 if (!do_not_delete) {
797 if (remove(dmpfile_pathname))
798 verbprintf(debug, "Unable to delete %s: %s\n", dmpfile_pathname,
802 list_for_each_safe(pos1, pos2, &jitdump_deletion_candidates) {
803 struct jitdump_deletion_candidate * pname = list_entry(pos1,
804 struct jitdump_deletion_candidate,
807 list_del(&pname->neighbor);
813 static void __print_usage(void)
815 fprintf(stderr, "usage: opjitconv [--debug | --non-root | --delete-jitdumps ] --session-dir=<dir> <starttime> <endtime>\n");
818 static int _process_args(int argc, char * const argv[])
821 int idx_of_non_options = 0;
822 char * prev_env = getenv("POSIXLY_CORRECT");
823 setenv("POSIXLY_CORRECT", "1", 0);
824 while (keep_trying) {
826 int c = getopt_long(argc, argv, short_options, long_options, &option_idx);
829 if (optind != argc) {
830 idx_of_non_options = optind;
835 printf("non-option detected at optind %d\n", optind);
837 idx_of_non_options = -1;
840 session_dir = optarg;
858 if (prev_env == NULL)
859 unsetenv("POSIXLY_CORRECT");
861 return idx_of_non_options;
864 int main(int argc, char * const argv[])
866 unsigned long long start_time, end_time;
867 struct stat filestat;
868 int non_options_idx, rc = 0;
869 size_t sessdir_len = 0;
875 non_options_idx = _process_args(argc, argv);
876 // We need the session_dir and two non-option values passed -- starttime and endtime.
877 if (!session_dir || (non_options_idx != argc - 2)) {
885 * Check for a maximum of 4096 bytes (Linux path name length limit) minus 16 bytes
886 * (to be used later for appending samples sub directory) minus 1 (for terminator).
887 * Integer overflows according to the session dir parameter (user controlled)
888 * are not possible anymore.
890 if ((sessdir_len = strlen(session_dir)) >= (PATH_MAX - 17)) {
891 printf("opjitconv: Path name length limit exceeded for session directory\n");
896 if (stat(session_dir, &filestat)) {
897 perror("stat operation on passed session-dir failed");
901 if (!S_ISDIR(filestat.st_mode)) {
902 printf("Passed session-dir %s is not a directory\n", session_dir);
907 start_time = atol(argv[non_options_idx++]);
908 end_time = atol(argv[non_options_idx]);
910 if (start_time > end_time) {
914 verbprintf(debug, "start time/end time is %llu/%llu\n",
915 start_time, end_time);
916 rc = op_process_jit_dumpfiles(session_dir, start_time, end_time);
920 if (rc > OP_JIT_CONV_OK) {
921 verbprintf(debug, "opjitconv: Ending with rc = %d. This code"
922 " is usually OK, but can be useful for debugging"
927 if (rc == OP_JIT_CONV_OK)