opjitconv.c

Go to the documentation of this file.
00001 
00017 #include "opjitconv.h"
00018 #include "opd_printf.h"
00019 #include "op_file.h"
00020 #include "op_libiberty.h"
00021 
00022 #include <dirent.h>
00023 #include <fnmatch.h>
00024 #include <errno.h>
00025 #include <fcntl.h>
00026 #include <limits.h>
00027 #include <pwd.h>
00028 #include <stdint.h>
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <sys/mman.h>
00033 #include <sys/types.h>
00034 #include <unistd.h>
00035 #include <wait.h>
00036 
00037 /*
00038  * list head.  The linked list is used during parsing (parse_all) to
00039  * hold all jitentry elements. After parsing, the program works on the
00040  * array structures (entries_symbols_ascending, entries_address_ascending)
00041  * and the linked list is not used any more.
00042  */
00043 struct jitentry * jitentry_list = NULL;
00044 struct jitentry_debug_line * jitentry_debug_line_list = NULL;
00045 
00046 /* Global variable for asymbols so we can free the storage later. */
00047 asymbol ** syms;
00048 
00049 /* jit dump header information */
00050 enum bfd_architecture dump_bfd_arch;
00051 int dump_bfd_mach;
00052 char const * dump_bfd_target_name;
00053 
00054 /* user information for special user 'oprofile' */
00055 struct passwd * pw_oprofile;
00056 
00057 char sys_cmd_buffer[PATH_MAX + 1];
00058 
00059 /* the bfd handle of the ELF file we write */
00060 bfd * cur_bfd;
00061 
00062 /* count of jitentries in the list */
00063 u32 entry_count;
00064 /* maximul space in the entry arrays, needed to add entries */
00065 u32 max_entry_count;
00066 /* array pointing to all jit entries, sorted by symbol names */
00067 struct jitentry ** entries_symbols_ascending;
00068 /* array pointing to all jit entries sorted by address */
00069 struct jitentry ** entries_address_ascending;
00070 
00071 /* debug flag, print some information */
00072 int debug;
00073 /* indicates opjitconv invoked by non-root user via operf */
00074 int non_root;
00075 /* indicates we should delete jitdump files owned by the user */
00076 int delete_jitdumps;
00077 LIST_HEAD(jitdump_deletion_candidates);
00078 
00079 /*
00080  *  Front-end processing from this point to end of the source.
00081  *    From main(), the general flow is as follows:
00082  *      1. Find all anonymous samples directories
00083  *      2. Find all JIT dump files
00084  *      3. For each JIT dump file:
00085  *        3.1 Find matching anon samples dir (from list retrieved in step 1)
00086  *        3.2 mmap the JIT dump file
00087  *        3.3 Call op_jit_convert to create ELF file if necessary
00088  */
00089 
00090 /* Callback function used for get_matching_pathnames() call to obtain
00091  * matching path names.
00092  */
00093 static void get_pathname(char const * pathname, void * name_list)
00094 {
00095     struct list_head * names = (struct list_head *) name_list;
00096     struct pathname * pn = xmalloc(sizeof(struct pathname));
00097     pn->name = xstrdup(pathname);
00098     list_add(&pn->neighbor, names);
00099 }
00100 
00101 static void delete_pathname(struct pathname * pname)
00102 {
00103     free(pname->name);
00104     list_del(&pname->neighbor);
00105     free(pname);
00106 }
00107 
00108 
00109 static void delete_path_names_list(struct list_head * list)
00110 {
00111     struct list_head * pos1, * pos2;
00112     list_for_each_safe(pos1, pos2, list) {
00113         struct pathname * pname = list_entry(pos1, struct pathname,
00114                              neighbor);
00115         delete_pathname(pname);
00116     }
00117 }
00118 
00119 static int mmap_jitdump(char const * dumpfile,
00120     struct op_jitdump_info * file_info)
00121 {
00122     int rc = OP_JIT_CONV_OK;
00123     int dumpfd;
00124 
00125     dumpfd = open(dumpfile, O_RDONLY);
00126     if (dumpfd < 0) {
00127         if (errno == ENOENT)
00128             rc = OP_JIT_CONV_NO_DUMPFILE;
00129         else
00130             rc = OP_JIT_CONV_FAIL;
00131         goto out;
00132     }
00133     rc = fstat(dumpfd, &file_info->dmp_file_stat);
00134     if (rc < 0) {
00135         perror("opjitconv:fstat on dumpfile");
00136         rc = OP_JIT_CONV_FAIL;
00137         goto out;
00138     }
00139     file_info->dmp_file = mmap(0, file_info->dmp_file_stat.st_size,
00140                    PROT_READ, MAP_PRIVATE, dumpfd, 0);
00141     if (file_info->dmp_file == MAP_FAILED) {
00142         perror("opjitconv:mmap\n");
00143         rc = OP_JIT_CONV_FAIL;
00144     }
00145 out:
00146     return rc;
00147 }
00148 
00149 static char const * find_anon_dir_match(struct list_head * anon_dirs,
00150                     char const * proc_id)
00151 {
00152     struct list_head * pos;
00153     char match_filter[10];
00154     snprintf(match_filter, 10, "*/%s.*", proc_id);
00155     list_for_each(pos, anon_dirs) {
00156         struct pathname * anon_dir =
00157             list_entry(pos, struct pathname, neighbor);
00158         if (!fnmatch(match_filter, anon_dir->name, 0))
00159             return anon_dir->name;
00160     }
00161     return NULL;
00162 }
00163 
00164 int change_owner(char * path)
00165 {
00166     int rc = OP_JIT_CONV_OK;
00167     int fd;
00168     
00169     if (non_root)
00170         return rc;
00171     fd = open(path, 0);
00172     if (fd < 0) {
00173         printf("opjitconv: File cannot be opened for changing ownership.\n");
00174         rc = OP_JIT_CONV_FAIL;
00175         goto out;
00176     }
00177     if (fchown(fd, pw_oprofile->pw_uid, pw_oprofile->pw_gid) != 0) {
00178         printf("opjitconv: Changing ownership failed (%s).\n", strerror(errno));
00179         close(fd);
00180         rc = OP_JIT_CONV_FAIL;
00181         goto out;
00182     }
00183     close(fd);
00184 
00185 out:
00186     return rc;
00187 }
00188 
00189 /* Copies the given file to the temporary working directory and sets ownership
00190  * to 'oprofile:oprofile'.
00191  */
00192 int copy_dumpfile(char const * dumpfile, char * tmp_dumpfile)
00193 {
00194     int rc = OP_JIT_CONV_OK;
00195 
00196     sprintf(sys_cmd_buffer, "/bin/cp -p %s %s", dumpfile, tmp_dumpfile);
00197 
00198     if (system(sys_cmd_buffer) != 0) {
00199         printf("opjitconv: Calling system() to copy files failed.\n");
00200         rc = OP_JIT_CONV_FAIL;
00201         goto out;
00202     }
00203 
00204     if (change_owner(tmp_dumpfile) != 0) {
00205         printf("opjitconv: Changing ownership of temporary dump file failed.\n");
00206         rc = OP_JIT_CONV_FAIL;
00207         goto out;
00208     }
00209     
00210 out:
00211     return rc;
00212 }
00213 
00214 /* Copies the created ELF file located in the temporary working directory to the
00215  * final destination (i.e. given ELF file name) and sets ownership to the
00216  * current user.
00217  */
00218 int copy_elffile(char * elf_file, char * tmp_elffile)
00219 {
00220     int rc = OP_JIT_CONV_OK;
00221     int fd;
00222 
00223     sprintf(sys_cmd_buffer, "/bin/cp -p %s %s", tmp_elffile, elf_file);
00224     if (system(sys_cmd_buffer) != 0) {
00225         printf("opjitconv: Calling system() to copy files failed.\n");
00226         rc = OP_JIT_CONV_FAIL;
00227         goto out;
00228     }
00229 
00230     fd = open(elf_file, 0);
00231     if (fd < 0) {
00232         printf("opjitconv: File cannot be opened for changing ownership.\n");
00233         rc = OP_JIT_CONV_FAIL;
00234         goto out;
00235     }
00236     if (fchown(fd, getuid(), getgid()) != 0) {
00237         printf("opjitconv: Changing ownership failed (%s).\n", strerror(errno));
00238         close(fd);
00239         rc = OP_JIT_CONV_FAIL;
00240         goto out;
00241     }
00242     close(fd);
00243     
00244 out:
00245     return rc;
00246 }
00247 
00248 /* Look for an anonymous samples directory that matches the process ID
00249  * given by the passed JIT dmp_pathname.  If none is found, it's an error
00250  * since by agreement, all JIT dump files should be removed every time
00251  * the user does --reset.  If we do find the matching samples directory,
00252  * we create an ELF file (<proc_id>.jo) and place it in that directory.
00253  */
00254 static int process_jit_dumpfile(char const * dmp_pathname,
00255                 struct list_head * anon_sample_dirs,
00256                 unsigned long long start_time,
00257                 unsigned long long end_time,
00258                 char * tmp_conv_dir)
00259 {
00260     int result_dir_length, proc_id_length;
00261     int rc = OP_JIT_CONV_OK;
00262     int jofd;
00263     struct stat file_stat;
00264     time_t dumpfile_modtime;
00265     struct op_jitdump_info dmp_info;
00266     char * elf_file = NULL;
00267     char * proc_id = NULL;
00268     char const * anon_dir;
00269     char const * dumpfilename = rindex(dmp_pathname, '/');
00270     /* temporary copy of dump file created for conversion step */
00271     char * tmp_dumpfile;
00272     /* temporary ELF file created during conversion step */
00273     char * tmp_elffile;
00274     
00275     verbprintf(debug, "Processing dumpfile %s\n", dmp_pathname);
00276     
00277     /* Check if the dump file is a symbolic link.
00278      * We should not trust symbolic links because we only produce normal dump
00279      * files (no links).
00280      */
00281     if (lstat(dmp_pathname, &file_stat) == -1) {
00282         printf("opjitconv: lstat for dumpfile failed (%s).\n", strerror(errno));
00283         rc = OP_JIT_CONV_FAIL;
00284         goto out;
00285     }
00286     if (S_ISLNK(file_stat.st_mode)) {
00287         printf("opjitconv: dumpfile path is corrupt (symbolic links not allowed).\n");
00288         rc = OP_JIT_CONV_FAIL;
00289         goto out;
00290     }
00291     
00292     if (dumpfilename) {
00293         size_t tmp_conv_dir_length = strlen(tmp_conv_dir);
00294         char const * dot_dump = rindex(++dumpfilename, '.');
00295         if (!dot_dump)
00296             goto chk_proc_id;
00297         proc_id_length = dot_dump - dumpfilename;
00298         proc_id = xmalloc(proc_id_length + 1);
00299         memcpy(proc_id, dumpfilename, proc_id_length);
00300         proc_id[proc_id_length] = '\0';
00301         verbprintf(debug, "Found JIT dumpfile for process %s\n",
00302                proc_id);
00303 
00304         tmp_dumpfile = xmalloc(tmp_conv_dir_length + 1 + strlen(dumpfilename) + 1);
00305         strncpy(tmp_dumpfile, tmp_conv_dir, tmp_conv_dir_length);
00306         tmp_dumpfile[tmp_conv_dir_length] = '\0';
00307         strcat(tmp_dumpfile, "/");
00308         strcat(tmp_dumpfile, dumpfilename);
00309     }
00310 chk_proc_id:
00311     if (!proc_id) {
00312         printf("opjitconv: dumpfile path is corrupt.\n");
00313         rc = OP_JIT_CONV_FAIL;
00314         goto out;
00315     }
00316     if (!(anon_dir = find_anon_dir_match(anon_sample_dirs, proc_id))) {
00317         /* With the capability of profiling with operf (as well as with
00318          * the legacy oprofile daemon), users will not be using opcontrol
00319          * to remove all jitdump files; instead, opjitconv will remove old
00320          * jitdump files (see _cleanup_jitdumps() for details).  But this cleanup
00321          * strategy makes it quite likely that opjitconv will sometimes find
00322          * jitdump files that are not owned by the current user or are in use
00323          * by other operf users, thus, the current profile data would not have
00324          * matching anon samples for such jitdump files.
00325          */
00326         verbprintf(debug, "Informational message: No matching anon samples for %s\n",
00327                    dmp_pathname);
00328         rc = OP_JIT_CONV_NO_MATCHING_ANON_SAMPLES;
00329         goto free_res1;
00330     }
00331     
00332     if (copy_dumpfile(dmp_pathname, tmp_dumpfile) != OP_JIT_CONV_OK)
00333         goto free_res1;
00334     
00335     if ((rc = mmap_jitdump(tmp_dumpfile, &dmp_info)) == OP_JIT_CONV_OK) {
00336         char * anon_path_seg = rindex(anon_dir, '/');
00337         if (!anon_path_seg) {
00338             printf("opjitconv: Bad path for anon sample: %s\n",
00339                    anon_dir);
00340             rc = OP_JIT_CONV_FAIL;
00341             goto free_res2;
00342         }
00343         result_dir_length = ++anon_path_seg - anon_dir;
00344         /* create final ELF file name */
00345         elf_file = xmalloc(result_dir_length +
00346                    strlen(proc_id) + strlen(".jo") + 1);
00347         strncpy(elf_file, anon_dir, result_dir_length);
00348         elf_file[result_dir_length] = '\0';
00349         strcat(elf_file, proc_id);
00350         strcat(elf_file, ".jo");
00351         /* create temporary ELF file name */
00352         tmp_elffile = xmalloc(strlen(tmp_conv_dir) + 1 +
00353                    strlen(proc_id) + strlen(".jo") + 1);
00354         strncpy(tmp_elffile, tmp_conv_dir, strlen(tmp_conv_dir));
00355         tmp_elffile[strlen(tmp_conv_dir)] = '\0';
00356         strcat(tmp_elffile, "/");
00357         strcat(tmp_elffile, proc_id);
00358         strcat(tmp_elffile, ".jo");
00359 
00360         // Check if final ELF file exists already
00361         jofd = open(elf_file, O_RDONLY);
00362         if (jofd < 0)
00363             goto create_elf;
00364         rc = fstat(jofd, &file_stat);
00365         if (rc < 0) {
00366             perror("opjitconv:fstat on .jo file");
00367             rc = OP_JIT_CONV_FAIL;
00368             goto free_res3;
00369         }
00370         if (dmp_info.dmp_file_stat.st_mtime >
00371             dmp_info.dmp_file_stat.st_ctime)
00372             dumpfile_modtime = dmp_info.dmp_file_stat.st_mtime;
00373         else
00374             dumpfile_modtime = dmp_info.dmp_file_stat.st_ctime;
00375 
00376         /* Final ELF file already exists, so if dumpfile has not been
00377          * modified since the ELF file's mod time, we don't need to
00378          * do ELF creation again.
00379          */
00380         if (!(file_stat.st_ctime < dumpfile_modtime ||
00381             file_stat.st_mtime < dumpfile_modtime)) {
00382             rc = OP_JIT_CONV_ALREADY_DONE;
00383             goto free_res3; 
00384         }
00385 
00386     create_elf:
00387         verbprintf(debug, "Converting %s to %s\n", dmp_pathname,
00388                elf_file);
00389         /* Set eGID of the special user 'oprofile'. */
00390         if (!non_root && setegid(pw_oprofile->pw_gid) != 0) {
00391             perror("opjitconv: setegid to special user failed");
00392             rc = OP_JIT_CONV_FAIL;
00393             goto free_res3;
00394         }
00395         /* Set eUID of the special user 'oprofile'. */
00396         if (!non_root && seteuid(pw_oprofile->pw_uid) != 0) {
00397             perror("opjitconv: seteuid to special user failed");
00398             rc = OP_JIT_CONV_FAIL;
00399             goto free_res3;
00400         }
00401         /* Convert the dump file as the special user 'oprofile'. */
00402         rc = op_jit_convert(dmp_info, tmp_elffile, start_time, end_time);
00403         /* Set eUID back to the original user. */
00404         if (!non_root && seteuid(getuid()) != 0) {
00405             perror("opjitconv: seteuid to original user failed");
00406             rc = OP_JIT_CONV_FAIL;
00407             goto free_res3;
00408         }
00409         /* Set eGID back to the original user. */
00410         if (!non_root && setegid(getgid()) != 0) {
00411             perror("opjitconv: setegid to original user failed");
00412             rc = OP_JIT_CONV_FAIL;
00413             goto free_res3;
00414         }
00415         rc = copy_elffile(elf_file, tmp_elffile);
00416     free_res3:
00417         free(elf_file);
00418         free(tmp_elffile);
00419     free_res2:
00420         munmap(dmp_info.dmp_file, dmp_info.dmp_file_stat.st_size);
00421     }
00422 free_res1:
00423     free(proc_id);
00424     free(tmp_dumpfile);
00425 out:
00426     return rc;
00427 }
00428 
00429 /* If non-NULL value is returned, caller is responsible for freeing memory.*/
00430 static char * get_procid_from_dirname(char * dirname)
00431 {
00432     char * ret = NULL;
00433     if (dirname) {
00434         char * proc_id;
00435         int proc_id_length;
00436         char * fname = rindex(dirname, '/');
00437         char const * dot = index(++fname, '.');
00438         if (!dot)
00439             goto out;
00440         proc_id_length = dot - fname;
00441         proc_id = xmalloc(proc_id_length + 1);
00442         memcpy(proc_id, fname, proc_id_length);
00443         proc_id[proc_id_length] = '\0';
00444         ret = proc_id;
00445     }
00446 out:
00447     return ret;
00448 }
00449 static void filter_anon_samples_list(struct list_head * anon_dirs)
00450 {
00451     struct procid {
00452         struct procid * next;
00453         char * pid;
00454     };
00455     struct procid * pid_list = NULL;
00456     struct procid * id, * nxt;
00457     struct list_head * pos1, * pos2;
00458     list_for_each_safe(pos1, pos2, anon_dirs) {
00459         struct pathname * pname = list_entry(pos1, struct pathname,
00460                              neighbor);
00461         char * proc_id = get_procid_from_dirname(pname->name);
00462         if (proc_id) {
00463             int found = 0;
00464             for (id = pid_list; id != NULL; id = id->next) {
00465                 if (!strcmp(id->pid, proc_id)) {
00466                     /* Already have an entry for this 
00467                      * process ID, so delete this entry
00468                      * from anon_dirs.
00469                      */
00470                     free(pname->name);
00471                     list_del(&pname->neighbor);
00472                     free(pname);
00473                     found = 1;
00474                 }
00475             }
00476             if (!found) {
00477                 struct procid * this_proc = 
00478                     xmalloc(sizeof(struct procid));
00479                 this_proc->pid = proc_id;
00480                 this_proc->next = pid_list;
00481                 pid_list = this_proc;
00482             }
00483         } else {
00484             printf("Unexpected result in processing anon sample"
00485                    " directory\n");
00486         }
00487     }
00488     for (id = pid_list; id; id = nxt) {
00489         free(id->pid);
00490         nxt = id->next;
00491         free(id);
00492     }
00493 }
00494 
00495 
00496 static void _add_jitdumps_to_deletion_list(void * all_jitdumps, char const * jitdump_dir )
00497 {
00498     struct list_head * jd_fnames = (struct list_head *) all_jitdumps;
00499     struct list_head * pos1, *pos2;
00500     size_t dir_len = strlen(jitdump_dir);
00501 
00502     list_for_each_safe(pos1, pos2, jd_fnames) {
00503         struct pathname * dmpfile =
00504                 list_entry(pos1, struct pathname, neighbor);
00505         struct stat mystat;
00506         char dmpfile_pathname[dir_len + 20];
00507         int fd;
00508         memset(dmpfile_pathname, '\0', dir_len + 20);
00509         strcpy(dmpfile_pathname, jitdump_dir);
00510         strcat(dmpfile_pathname,dmpfile->name);
00511         fd = open(dmpfile_pathname, O_RDONLY);
00512         if (fd < 0) {
00513             // Non-fatal error, so just display debug message and continue
00514             verbprintf(debug, "opjitconv: cannot open jitdump file %s\n",
00515                        dmpfile_pathname);
00516             continue;
00517         }
00518         if (fstat(fd, &mystat) < 0) {
00519             // Non-fatal error, so just display debug message and continue
00520             verbprintf(debug, "opjitconv: cannot fstat jitdump file");
00521             continue;
00522         }
00523         if (geteuid() == mystat.st_uid) {
00524             struct jitdump_deletion_candidate * jdc =
00525                     xmalloc(sizeof(struct jitdump_deletion_candidate));
00526             jdc->name = xstrdup(dmpfile->name);
00527             list_add(&jdc->neighbor, &jitdump_deletion_candidates);
00528         }
00529     }
00530 }
00531 
00532 static int op_process_jit_dumpfiles(char const * session_dir,
00533     unsigned long long start_time, unsigned long long end_time)
00534 {
00535     struct list_head * pos1, * pos2;
00536     int rc = OP_JIT_CONV_OK;
00537     char jitdumpfile[PATH_MAX + 1];
00538     char oprofile_tmp_template[PATH_MAX + 1];
00539     char const * jitdump_dir = "/var/lib/oprofile/jitdump/";
00540 
00541     LIST_HEAD(jd_fnames);
00542     char const * anon_dir_filter = "*/{dep}/{anon:anon}/[0-9]*.*";
00543     LIST_HEAD(anon_dnames);
00544     char const * samples_subdir = "/samples/current";
00545     int samples_dir_len = strlen(session_dir) + strlen(samples_subdir);
00546     char * samples_dir;
00547     /* temporary working directory for dump file conversion step */
00548     char * tmp_conv_dir = NULL;
00549 
00550     if (non_root)
00551         sprintf(oprofile_tmp_template, "%s/tmp", session_dir);
00552     else
00553         strcpy(oprofile_tmp_template, "/tmp/oprofile.XXXXXX");
00554 
00555     /* Create a temporary working directory used for the conversion step.
00556      */
00557     if (non_root) {
00558         sprintf(sys_cmd_buffer, "/bin/rm -rf %s", oprofile_tmp_template);
00559         if (system(sys_cmd_buffer) != 0) {
00560             printf("opjitconv: Removing temporary working directory %s failed.\n",
00561                    oprofile_tmp_template);
00562             rc = OP_JIT_CONV_TMPDIR_NOT_REMOVED;
00563         } else {
00564             if (!mkdir(oprofile_tmp_template, S_IRWXU | S_IRWXG ))
00565                 tmp_conv_dir = oprofile_tmp_template;
00566         }
00567     } else {
00568         tmp_conv_dir = mkdtemp(oprofile_tmp_template);
00569     }
00570 
00571     if (tmp_conv_dir == NULL) {
00572         printf("opjitconv: Temporary working directory %s cannot be created.\n",
00573                oprofile_tmp_template);
00574         perror("Exiting due to error");
00575         rc = OP_JIT_CONV_FAIL;
00576         goto out;
00577     }
00578 
00579 
00580     errno = 0;
00581     if ((rc = get_matching_pathnames(&jd_fnames, get_pathname,
00582         jitdump_dir, "*.dump", NO_RECURSION)) < 0
00583             || list_empty(&jd_fnames)) {
00584         if (errno) {
00585             if (errno != ENOENT) {
00586                 char msg[PATH_MAX];
00587                 strcpy(msg, "opjitconv: fatal error trying to find JIT dump files in ");
00588                 strcat(msg, jitdump_dir);
00589                 perror(msg);
00590                 rc = OP_JIT_CONV_FAIL;
00591             } else {
00592                 verbprintf(debug, "opjitconv: Non-fatal error trying to find JIT dump files in %s: %s\n",
00593                            jitdump_dir, strerror(errno));
00594                 rc = OP_JIT_CONV_NO_DUMPFILE;
00595             }
00596         }
00597         goto rm_tmp;
00598     }
00599 
00600     if (delete_jitdumps)
00601         _add_jitdumps_to_deletion_list(&jd_fnames, jitdump_dir);
00602 
00603     /* Get user information (i.e. UID and GID) for special user 'oprofile'.
00604      */
00605     if (non_root) {
00606         pw_oprofile = NULL;
00607     } else {
00608         pw_oprofile = getpwnam("oprofile");
00609         if (pw_oprofile == NULL) {
00610             printf("opjitconv: User information for special user oprofile cannot be found.\n");
00611             rc = OP_JIT_CONV_FAIL;
00612             goto rm_tmp;
00613         }
00614     }
00615 
00616     /* Change ownership of the temporary working directory to prevent other users
00617      * to attack conversion process.
00618      */
00619     if (change_owner(tmp_conv_dir) != 0) {
00620         printf("opjitconv: Changing ownership of temporary directory failed.\n");
00621         rc = OP_JIT_CONV_FAIL;
00622         goto rm_tmp;
00623     }
00624     
00625     samples_dir = xmalloc(samples_dir_len + 1);
00626     sprintf(samples_dir, "%s%s", session_dir, samples_subdir);
00627     if (get_matching_pathnames(&anon_dnames, get_pathname,
00628                     samples_dir, anon_dir_filter,
00629                     MATCH_DIR_ONLY_RECURSION) < 0
00630         || list_empty(&anon_dnames)) {
00631         rc = OP_JIT_CONV_NO_ANON_SAMPLES;
00632         goto rm_tmp;
00633     }
00634     /* When using get_matching_pathnames to find anon samples,
00635      * the list that's returned may contain multiple entries for
00636      * one or more processes; e.g.,
00637      *    6868.0x100000.0x103000
00638      *    6868.0xdfe77000.0xdec40000
00639      *    7012.0x100000.0x103000
00640      *    7012.0xdfe77000.0xdec40000
00641      *
00642      * So we must filter the list so there's only one entry per
00643      * process.
00644      */
00645     filter_anon_samples_list(&anon_dnames);
00646 
00647     /* get_matching_pathnames returns only filename segment when
00648      * NO_RECURSION is passed, so below, we add back the JIT
00649      * dump directory path to the name.
00650      */
00651     list_for_each_safe(pos1, pos2, &jd_fnames) {
00652         struct pathname * dmpfile =
00653             list_entry(pos1, struct pathname, neighbor);
00654         strncpy(jitdumpfile, jitdump_dir, PATH_MAX);
00655         strncat(jitdumpfile, dmpfile->name, PATH_MAX);
00656         rc = process_jit_dumpfile(jitdumpfile, &anon_dnames,
00657                       start_time, end_time, tmp_conv_dir);
00658         if (rc == OP_JIT_CONV_FAIL) {
00659             verbprintf(debug, "JIT convert error %d\n", rc);
00660             goto rm_tmp;
00661         }
00662         delete_pathname(dmpfile);
00663     }
00664     delete_path_names_list(&anon_dnames);
00665     
00666 rm_tmp:
00667     /* Delete temporary working directory with all its files
00668      * (i.e. dump and ELF file).
00669      */
00670     sprintf(sys_cmd_buffer, "/bin/rm -rf %s", tmp_conv_dir);
00671     if (system(sys_cmd_buffer) != 0) {
00672         printf("opjitconv: Removing temporary working directory failed.\n");
00673         rc = OP_JIT_CONV_TMPDIR_NOT_REMOVED;
00674     }
00675     
00676 out:
00677     return rc;
00678 }
00679 
00680 static void _cleanup_jitdumps(void)
00681 {
00682     struct list_head * pos1, *pos2;
00683     char const * jitdump_dir = "/var/lib/oprofile/jitdump/";
00684     size_t dir_len = strlen(jitdump_dir);
00685     char dmpfile_pathname[dir_len + 20];
00686     char proc_fd_dir[PATH_MAX];
00687 
00688     if (!delete_jitdumps)
00689         return;
00690 
00691     /* The delete_jitdumps flag tells us to try to delete the jitdump files we found
00692      * that belong to this user.  Only operf should pass the --delete-jitdumps
00693      * argument to opjitconv since legacy oprofile uses opcontrol to delete old
00694      * jitdump files.
00695      *
00696      * The code below will only delete jitdump files that are not currently
00697      * being used by another process.
00698      */
00699     list_for_each_safe(pos1, pos2, &jitdump_deletion_candidates) {
00700         DIR * dir;
00701         struct dirent * dirent;
00702         int pid;
00703         size_t dmpfile_name_len;
00704         int do_not_delete = 0;
00705         struct jitdump_deletion_candidate * cand = list_entry(pos1,
00706                                                               struct jitdump_deletion_candidate,
00707                                                               neighbor);
00708         memset(dmpfile_pathname, '\0', dir_len + 20);
00709         memset(proc_fd_dir, '\0', PATH_MAX);
00710 
00711         if ((sscanf(cand->name, "%d", &pid)) != 1) {
00712             verbprintf(debug, "Cannot get process id from jitdump file %s\n",
00713                        cand->name);
00714             continue;
00715         }
00716 
00717         strcpy(dmpfile_pathname, jitdump_dir);
00718         strcat(dmpfile_pathname, cand->name);
00719         dmpfile_name_len = strlen(dmpfile_pathname);
00720 
00721         sprintf(proc_fd_dir, "/proc/%d/fd/", pid);
00722         if ((dir = opendir(proc_fd_dir))) {
00723             size_t proc_fd_dir_len = strlen(proc_fd_dir);
00724             while ((dirent = readdir(dir))) {
00725                 if (dirent->d_type == DT_LNK) {
00726                     char buf[1024];
00727                     char fname[1024];
00728                     memset(fname, '\0', 1024);
00729                     strcpy(fname, proc_fd_dir);
00730                     strncat(fname, dirent->d_name, 1023 - proc_fd_dir_len);
00731                     if (readlink(fname, buf, 1023) > 0) {
00732                         verbprintf(debug, "readlink found for %s\n", buf);
00733                         if (strncmp(buf, dmpfile_pathname,
00734                                     dmpfile_name_len) == 0) {
00735                             do_not_delete = 1;
00736                             break;
00737                         }
00738                     }
00739                 }
00740             }
00741         }
00742         if (!do_not_delete)
00743             remove(dmpfile_pathname);
00744     }
00745     list_for_each_safe(pos1, pos2, &jitdump_deletion_candidates) {
00746         struct jitdump_deletion_candidate * pname = list_entry(pos1,
00747                                                                struct jitdump_deletion_candidate,
00748                                                                neighbor);
00749         free(pname->name);
00750         list_del(&pname->neighbor);
00751         free(pname);
00752     }
00753 
00754 }
00755 
00756 int main(int argc, char ** argv)
00757 {
00758     unsigned long long start_time, end_time;
00759     char const * session_dir;
00760     int rc = 0;
00761 
00762     debug = 0;
00763     if (argc > 1 && strcmp(argv[1], "-d") == 0) {
00764         debug = 1;
00765         argc--;
00766         argv++;
00767     }
00768     non_root = 0;
00769     if (argc > 1 && strcmp(argv[1], "--non-root") == 0) {
00770         non_root = 1;
00771         argc--;
00772         argv++;
00773     }
00774 
00775     delete_jitdumps = 0;
00776     if (argc > 1 && strcmp(argv[1], "--delete-jitdumps") == 0) {
00777         delete_jitdumps = 1;
00778         argc--;
00779         argv++;
00780     }
00781 
00782     if (argc != 4) {
00783         printf("Usage: opjitconv [-d] <session_dir> <starttime>"
00784                " <endtime>\n");
00785         fflush(stdout);
00786         rc = EXIT_FAILURE;
00787         goto out;
00788     }
00789 
00790     session_dir = argv[1];
00791     /*
00792      * Check for a maximum of 4096 bytes (Linux path name length limit) decremented
00793      * by 16 bytes (will be used later for appending samples sub directory).
00794      * Integer overflows according to the session dir parameter (user controlled)
00795      * are not possible anymore.
00796      */
00797     if (strlen(session_dir) > PATH_MAX - 16) {
00798         printf("opjitconv: Path name length limit exceeded for session directory: %s\n", session_dir);
00799         rc = EXIT_FAILURE;
00800         goto out;
00801     }
00802 
00803     start_time = atol(argv[2]);
00804     end_time = atol(argv[3]);
00805 
00806     if (start_time > end_time) {
00807         rc = EXIT_FAILURE;
00808         goto out;
00809     }
00810     verbprintf(debug, "start time/end time is %llu/%llu\n",
00811            start_time, end_time);
00812     rc = op_process_jit_dumpfiles(session_dir, start_time, end_time);
00813     if (delete_jitdumps)
00814         _cleanup_jitdumps();
00815 
00816     if (rc > OP_JIT_CONV_OK) {
00817         verbprintf(debug, "opjitconv: Ending with rc = %d. This code"
00818                " is usually OK, but can be useful for debugging"
00819                " purposes.\n", rc);
00820         rc = OP_JIT_CONV_OK;
00821     }
00822     fflush(stdout);
00823     if (rc == OP_JIT_CONV_OK)
00824         rc = EXIT_SUCCESS;
00825     else
00826         rc = EXIT_FAILURE;
00827 out:
00828     _exit(rc);
00829 }

Generated on 8 Nov 2012 for Oprofile by  doxygen 1.6.1