Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame] | 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 | } |