XDEBUG_NOTIMPLEMENTED static Variant HHVM_FUNCTION(xdebug_start_trace, const Variant& traceFileVar, int64_t options /* = 0 */) { // Allowed to pass null. folly::StringPiece trace_file; if (traceFileVar.isString()) { // We're not constructing a new String, we're just using the one in // traceFileVar, so this is safe. trace_file = traceFileVar.toString().slice(); } // Initialize the profiler if it isn't already. if (!XDEBUG_GLOBAL(ProfilerAttached)) { attach_xdebug_profiler(); } // php5 xdebug returns false when tracing already started. auto profiler = xdebug_profiler(); if (profiler->isTracing()) { return false; } // Start tracing, then grab the current begin frame start_tracing(profiler, trace_file, options); profiler->beginFrame(nullptr); return HHVM_FN(xdebug_get_tracefile_name)(); }
int main(int argc, char *argv[]) { errval_t err; coreid_t mycore = disp_get_core_id(); debug_printf("This is mem_bench\n"); if (argc >= 2) { assert(mycore == 0); int num_cores = strtol(argv[1], NULL, 10); debug_printf("spawning on %d cores\n", num_cores); err = init_tracing(); if (err_is_fail(err)) { DEBUG_ERR(err, "initialising tracing"); return EXIT_FAILURE; } prepare_dump(); start_tracing(); char *path = argv[0]; argv[1] = NULL; for (int i = 1; i <= num_cores; i++) { err = spawn_program(i, path, argv, NULL, 0, NULL); if (err_is_fail(err)) { DEBUG_ERR(err, "failed spawn %d", i); return EXIT_FAILURE; } debug_printf("spawned on core %d\n", i); } //start_tracing(); run_benchmark_0(mycore); ns_barrier_master(1, num_cores, "mem_bench"); debug_printf("all benchmarks completed\n"); stop_tracing(); // dump_trace(); } else { run_benchmark(mycore); } return EXIT_SUCCESS; }
// Attempts to attach the xdebug profiler to the current thread. Assumes it // is not already attached. Raises an error on failure. static void attach_xdebug_profiler() { assert(!XDEBUG_GLOBAL(ProfilerAttached)); if (s_profiler_factory->start(ProfilerKind::XDebug, 0, false)) { XDEBUG_GLOBAL(ProfilerAttached) = true; // Enable profiling and tracing if we need to auto profiler = xdebug_profiler(); if (XDebugProfiler::isProfilingNeeded()) { start_profiling(profiler); } if (XDebugProfiler::isTracingNeeded()) { start_tracing(profiler); } profiler->setCollectMemory(XDEBUG_GLOBAL(CollectMemory)); profiler->setCollectTime(XDEBUG_GLOBAL(CollectTime)); profiler->setMaxNestingLevel(XDEBUG_GLOBAL(MaxNestingLevel)); } else { raise_error("Could not start xdebug profiler. Another profiler is " "likely already attached to this thread."); } }
/* I/O write */ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value) { trace_dev_state *s = (trace_dev_state *)opaque; switch (offset >> 2) { case TRACE_DEV_REG_SWITCH: // context switch, switch to pid if (trace_filename != NULL) { trace_switch(value); #ifdef DEBUG printf("QEMU.trace: kernel, context switch %u\n", value); #endif } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_switch(value); } #endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_TGID: // save the tgid for the following fork/clone tgid = value; #ifdef DEBUG if (trace_filename != NULL) { printf("QEMU.trace: kernel, tgid %u\n", value); } #endif break; case TRACE_DEV_REG_FORK: // fork, fork new pid if (trace_filename != NULL) { trace_fork(tgid, value); #ifdef DEBUG printf("QEMU.trace: kernel, fork %u\n", value); #endif } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_fork(tgid, value); } #endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_CLONE: // fork, clone new pid (i.e. thread) if (trace_filename != NULL) { trace_clone(tgid, value); #ifdef DEBUG printf("QEMU.trace: kernel, clone %u\n", value); #endif } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_clone(tgid, value); } #endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_EXECVE_VMSTART: // execve, vstart vstart = value; break; case TRACE_DEV_REG_EXECVE_VMEND: // execve, vend vend = value; break; case TRACE_DEV_REG_EXECVE_OFFSET: // execve, offset in EXE eoff = value; break; case TRACE_DEV_REG_EXECVE_EXEPATH: // init exec, path of EXE vstrcpy(value, path, CLIENT_PAGE_SIZE); if (trace_filename != NULL) { trace_init_exec(vstart, vend, eoff, path); #ifdef DEBUG printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); #endif } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { if (path[0] == '\0') { // vstrcpy may fail to copy path. In this case lets do it // differently. memcheck_get_guest_kernel_string(path, value, CLIENT_PAGE_SIZE); } memcheck_mmap_exepath(vstart, vend, eoff, path); } #endif // CONFIG_MEMCHECK path[0] = 0; break; case TRACE_DEV_REG_CMDLINE_LEN: // execve, process cmdline length cmdlen = value; break; case TRACE_DEV_REG_CMDLINE: // execve, process cmdline cpu_memory_rw_debug(cpu_single_env, value, arg, cmdlen, 0); if (trace_filename != NULL) { trace_execve(arg, cmdlen); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_set_cmd_line(arg, cmdlen); } #endif // CONFIG_MEMCHECK #ifdef DEBUG if (trace_filename != NULL) { int i; for (i = 0; i < cmdlen; i ++) if (i != cmdlen - 1 && arg[i] == 0) arg[i] = ' '; printf("QEMU.trace: kernel, execve %s[%d]\n", arg, cmdlen); arg[0] = 0; } #endif break; case TRACE_DEV_REG_EXIT: // exit, exit current process with exit code if (trace_filename != NULL) { trace_exit(value); #ifdef DEBUG printf("QEMU.trace: kernel, exit %x\n", value); #endif } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_exit(value); } #endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_NAME: // record thread name vstrcpy(value, path, CLIENT_PAGE_SIZE); // Remove the trailing newline if it exists int len = strlen(path); if (path[len - 1] == '\n') { path[len - 1] = 0; } if (trace_filename != NULL) { trace_name(path); #ifdef DEBUG printf("QEMU.trace: kernel, name %s\n", path); #endif } break; case TRACE_DEV_REG_MMAP_EXEPATH: // mmap, path of EXE, the others are same as execve vstrcpy(value, path, CLIENT_PAGE_SIZE); if (trace_filename != NULL) { trace_mmap(vstart, vend, eoff, path); #ifdef DEBUG printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); #endif } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { if (path[0] == '\0') { // vstrcpy may fail to copy path. In this case lets do it // differently. memcheck_get_guest_kernel_string(path, value, CLIENT_PAGE_SIZE); } memcheck_mmap_exepath(vstart, vend, eoff, path); } #endif // CONFIG_MEMCHECK path[0] = 0; break; case TRACE_DEV_REG_INIT_PID: // init, name the pid that starts before device registered pid = value; #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_init_pid(value); } #endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_INIT_NAME: // init, the comm of the init pid vstrcpy(value, path, CLIENT_PAGE_SIZE); if (trace_filename != NULL) { trace_init_name(tgid, pid, path); #ifdef DEBUG printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path); #endif } path[0] = 0; break; case TRACE_DEV_REG_DYN_SYM_ADDR: // dynamic symbol address dsaddr = value; break; case TRACE_DEV_REG_DYN_SYM: // add dynamic symbol vstrcpy(value, arg, CLIENT_PAGE_SIZE); if (trace_filename != NULL) { trace_dynamic_symbol_add(dsaddr, arg); #ifdef DEBUG printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg); #endif } arg[0] = 0; break; case TRACE_DEV_REG_REMOVE_ADDR: // remove dynamic symbol addr if (trace_filename != NULL) { trace_dynamic_symbol_remove(value); #ifdef DEBUG printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr); #endif } break; case TRACE_DEV_REG_PRINT_STR: // print string vstrcpy(value, arg, CLIENT_PAGE_SIZE); printf("%s", arg); arg[0] = 0; break; case TRACE_DEV_REG_PRINT_NUM_DEC: // print number in decimal printf("%d", value); break; case TRACE_DEV_REG_PRINT_NUM_HEX: // print number in hexical printf("%x", value); break; case TRACE_DEV_REG_STOP_EMU: // stop the VM execution if (trace_filename != NULL) { // To ensure that the number of instructions executed in this // block is correct, we pretend that there was an exception. trace_exception(0); } cpu_single_env->exception_index = EXCP_HLT; cpu_single_env->halted = 1; qemu_system_shutdown_request(); cpu_loop_exit(); break; case TRACE_DEV_REG_ENABLE: // tracing enable: 0 = stop, 1 = start if (value == 1) { if (trace_filename != NULL) { start_tracing(); } } else if (value == 0) { if (trace_filename != NULL) { stop_tracing(); // To ensure that the number of instructions executed in this // block is correct, we pretend that there was an exception. trace_exception(0); } } break; case TRACE_DEV_REG_UNMAP_START: unmap_start = value; break; case TRACE_DEV_REG_UNMAP_END: if (trace_filename != NULL) { trace_munmap(unmap_start, value); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_unmap(unmap_start, value); } #endif // CONFIG_MEMCHECK break; case TRACE_DEV_REG_METHOD_ENTRY: case TRACE_DEV_REG_METHOD_EXIT: case TRACE_DEV_REG_METHOD_EXCEPTION: case TRACE_DEV_REG_NATIVE_ENTRY: case TRACE_DEV_REG_NATIVE_EXIT: case TRACE_DEV_REG_NATIVE_EXCEPTION: if (trace_filename != NULL) { if (tracing) { int call_type = (offset - 4096) >> 2; trace_interpreted_method(value, call_type); } } break; #ifdef CONFIG_MEMCHECK case TRACE_DEV_REG_MALLOC: if (memcheck_enabled) { memcheck_guest_alloc(value); } break; case TRACE_DEV_REG_FREE_PTR: if (memcheck_enabled) { memcheck_guest_free(value); } break; case TRACE_DEV_REG_QUERY_MALLOC: if (memcheck_enabled) { memcheck_guest_query_malloc(value); } break; case TRACE_DEV_REG_LIBC_INIT: if (memcheck_enabled) { memcheck_guest_libc_initialized(value); } break; case TRACE_DEV_REG_PRINT_USER_STR: if (memcheck_enabled) { memcheck_guest_print_str(value); } break; #endif // CONFIG_MEMCHECK default: if (offset < 4096) { cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset); } break; } }
int main(int ac, char **av) { struct sigaction sa; struct trussinfo *trussinfo; char *fname; char **command; pid_t pid; int c; fname = NULL; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "calloc() failed"); pid = 0; trussinfo->outfile = stderr; trussinfo->strsize = 32; trussinfo->curthread = NULL; LIST_INIT(&trussinfo->proclist); init_syscalls(); while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) { switch (c) { case 'p': /* specified pid */ pid = atoi(optarg); /* make sure i don't trace me */ if (pid == getpid()) { errx(2, "attempt to grab self."); } break; case 'f': /* Follow fork()'s */ trussinfo->flags |= FOLLOWFORKS; break; case 'a': /* Print execve() argument strings. */ trussinfo->flags |= EXECVEARGS; break; case 'c': /* Count number of system calls and time. */ trussinfo->flags |= (COUNTONLY | NOSIGS); break; case 'e': /* Print execve() environment strings. */ trussinfo->flags |= EXECVEENVS; break; case 'd': /* Absolute timestamps */ trussinfo->flags |= ABSOLUTETIMESTAMPS; break; case 'D': /* Relative timestamps */ trussinfo->flags |= RELATIVETIMESTAMPS; break; case 'o': /* Specified output file */ fname = optarg; break; case 's': /* Specified string size */ trussinfo->strsize = atoi(optarg); break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; case 'H': trussinfo->flags |= DISPLAYTIDS; break; default: usage(); } } ac -= optind; av += optind; if ((pid == 0 && ac == 0) || (pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ /* * Set close-on-exec ('e'), so that the output file is not * shared with the traced process. */ if ((trussinfo->outfile = fopen(fname, "we")) == NULL) err(1, "cannot open %s", fname); } /* * If truss starts the process itself, it will ignore some signals -- * they should be passed off to the process, which may or may not * exit. If, however, we are examining an already-running process, * then we restore the event mask on these same signals. */ if (pid == 0) { /* Start a command ourselves */ command = av; setup_and_wait(trussinfo, command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); } else { sa.sa_handler = restore_proc; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); start_tracing(trussinfo, pid); } /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) { /* * If we are not able to handle this ABI, detach from the * process and exit. If we just created a new process to * run a command, kill the new process rather than letting * it run untraced. */ if (pid == 0) kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL); ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL, 0); return (1); } ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1, 0); /* * At this point, it's a simple loop, waiting for the process to * stop, finding out why, printing out why, and then continuing it. * All of the grunt work is done in the support routines. */ clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); eventloop(trussinfo); if (trussinfo->flags & COUNTONLY) print_summary(trussinfo); fflush(trussinfo->outfile); return (0); }
int main(int ac, char **av) { int c, i, mntsize; char **command; struct procfs_status pfs; struct ex_types *funcs; struct statfs *mntbuf; int in_exec = 0; char *fname = NULL; int sigexit = 0; struct trussinfo *trussinfo; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)malloc(sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "malloc() failed"); bzero(trussinfo, sizeof(struct trussinfo)); trussinfo->outfile = stderr; /* Check where procfs is mounted if it is mounted */ if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) err(1, "getmntinfo"); for (i = 0; i < mntsize; i++) { if (strcasecmp(mntbuf[i].f_mntfromname, "procfs") == 0) { strlcpy(procfs_path, mntbuf[i].f_mntonname, sizeof(procfs_path)); have_procfs = 1; break; } } if (!have_procfs) { errno = 2; err(1, "You must have a mounted procfs to use truss"); } while ((c = getopt(ac, av, "p:o:S")) != -1) { switch (c) { case 'p': /* specified pid */ trussinfo->pid = atoi(optarg); if (trussinfo->pid == getpid()) { /* make sure truss doesn't trace itself */ fprintf(stderr, "truss: attempt to self trace: %d\n", trussinfo->pid); exit(2); } break; case 'o': /* Specified output file */ fname = optarg; break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; default: usage(); } } ac -= optind; av += optind; if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ if ((trussinfo->outfile = fopen(fname, "w")) == NULL) errx(1, "cannot open %s", fname); } /* * If truss starts the process itself, it will ignore some signals -- * they should be passed off to the process, which may or may not * exit. If, however, we are examining an already-running process, * then we restore the event mask on these same signals. */ if (trussinfo->pid == 0) { /* Start a command ourselves */ command = av; trussinfo->pid = setup_and_wait(command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); } else { signal(SIGINT, restore_proc); signal(SIGTERM, restore_proc); signal(SIGQUIT, restore_proc); } /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ Procfd = start_tracing( trussinfo->pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT | ((trussinfo->flags & NOSIGS) ? 0 : S_SIG)); if (Procfd == -1) return 0; pfs.why = 0; funcs = set_etype(trussinfo); /* * At this point, it's a simple loop, waiting for the process to * stop, finding out why, printing out why, and then continuing it. * All of the grunt work is done in the support routines. */ do { int val = 0; if (ioctl(Procfd, PIOCWAIT, &pfs) == -1) warn("PIOCWAIT top of loop"); else { switch(i = pfs.why) { case S_SCE: funcs->enter_syscall(trussinfo, pfs.val); break; case S_SCX: /* * This is so we don't get two messages for an exec -- one * for the S_EXEC, and one for the syscall exit. It also, * conveniently, ensures that the first message printed out * isn't the return-from-syscall used to create the process. */ if (in_exec) { in_exec = 0; break; } funcs->exit_syscall(trussinfo, pfs.val); break; case S_SIG: fprintf(trussinfo->outfile, "SIGNAL %lu\n", pfs.val); sigexit = pfs.val; break; case S_EXIT: fprintf (trussinfo->outfile, "process exit, rval = %lu\n", pfs.val); break; case S_EXEC: funcs = set_etype(trussinfo); in_exec = 1; break; default: fprintf (trussinfo->outfile, "Process stopped because of: %d\n", i); break; } } if (ioctl(Procfd, PIOCCONT, val) == -1) { if (kill(trussinfo->pid, 0) == -1 && errno == ESRCH) break; else warn("PIOCCONT"); } } while (pfs.why != S_EXIT); fflush(trussinfo->outfile); if (sigexit) { if (sigexit == SIGQUIT) exit(sigexit); (void) signal(sigexit, SIG_DFL); (void) kill(getpid(), sigexit); } return 0; }
/* Handles UI control command received from the UI. * Param: * corecmd - CoreCmdImpl instance that received the command. * cmd_header - Command header. * cmd_param - Command data. */ static void _coreCmdImpl_handle_command(CoreCmdImpl* corecmd, const UICmdHeader* cmd_header, const uint8_t* cmd_param) { switch (cmd_header->cmd_type) { case AUICMD_SET_COARSE_ORIENTATION: { UICmdSetCoarseOrientation* cmd = (UICmdSetCoarseOrientation*)cmd_param; android_sensors_set_coarse_orientation(cmd->orient); break; } case AUICMD_TOGGLE_NETWORK: qemu_net_disable = !qemu_net_disable; if (android_modem) { amodem_set_data_registration( android_modem, qemu_net_disable ? A_REGISTRATION_UNREGISTERED : A_REGISTRATION_HOME); } break; case AUICMD_TRACE_CONTROL: { UICmdTraceControl* cmd = (UICmdTraceControl*)cmd_param; if (cmd->start) { start_tracing(); } else { stop_tracing(); } break; } case AUICMD_CHK_NETWORK_DISABLED: { UICmdRespHeader resp; resp.resp_data_size = 0; resp.result = qemu_net_disable; _coreCmdImpl_respond(corecmd, &resp, NULL); break; } case AUICMD_GET_NETSPEED: { UICmdRespHeader resp; UICmdGetNetSpeedResp* resp_data = NULL; UICmdGetNetSpeed* cmd = (UICmdGetNetSpeed*)cmd_param; resp.resp_data_size = 0; resp.result = 0; if (cmd->index >= android_netspeeds_count || android_netspeeds[cmd->index].name == NULL) { resp.result = -1; } else { const NetworkSpeed* netspeed = &android_netspeeds[cmd->index]; // Calculate size of the response data: // fixed header + zero-terminated netspeed name. resp.resp_data_size = sizeof(UICmdGetNetSpeedResp) + strlen(netspeed->name) + 1; // Count in zero-terminated netspeed display. if (netspeed->display != NULL) { resp.resp_data_size += strlen(netspeed->display) + 1; } else { resp.resp_data_size++; } // Allocate and initialize response data buffer. resp_data = (UICmdGetNetSpeedResp*)qemu_malloc(resp.resp_data_size); resp_data->upload = netspeed->upload; resp_data->download = netspeed->download; strcpy(resp_data->name, netspeed->name); if (netspeed->display != NULL) { strcpy(resp_data->name + strlen(resp_data->name) + 1, netspeed->display); } else { strcpy(resp_data->name + strlen(resp_data->name) + 1, ""); } } _coreCmdImpl_respond(corecmd, &resp, resp_data); if (resp_data != NULL) { qemu_free(resp_data); } break; } case AUICMD_GET_NETDELAY: { UICmdRespHeader resp; UICmdGetNetDelayResp* resp_data = NULL; UICmdGetNetDelay* cmd = (UICmdGetNetDelay*)cmd_param; resp.resp_data_size = 0; resp.result = 0; if (cmd->index >= android_netdelays_count || android_netdelays[cmd->index].name == NULL) { resp.result = -1; } else { const NetworkLatency* netdelay = &android_netdelays[cmd->index]; // Calculate size of the response data: // fixed header + zero-terminated netdelay name. resp.resp_data_size = sizeof(UICmdGetNetDelayResp) + strlen(netdelay->name) + 1; // Count in zero-terminated netdelay display. if (netdelay->display != NULL) { resp.resp_data_size += strlen(netdelay->display) + 1; } else { resp.resp_data_size++; } // Allocate and initialize response data buffer. resp_data = (UICmdGetNetDelayResp*)qemu_malloc(resp.resp_data_size); resp_data->min_ms = netdelay->min_ms; resp_data->max_ms = netdelay->max_ms; strcpy(resp_data->name, netdelay->name); if (netdelay->display != NULL) { strcpy(resp_data->name + strlen(resp_data->name) + 1, netdelay->display); } else { strcpy(resp_data->name + strlen(resp_data->name) + 1, ""); } } _coreCmdImpl_respond(corecmd, &resp, resp_data); if (resp_data != NULL) { qemu_free(resp_data); } break; } case AUICMD_GET_QEMU_PATH: { UICmdRespHeader resp; UICmdGetQemuPath* cmd = (UICmdGetQemuPath*)cmd_param; char* filepath = NULL; resp.resp_data_size = 0; resp.result = -1; filepath = qemu_find_file(cmd->type, cmd->filename); if (filepath != NULL) { resp.resp_data_size = strlen(filepath) + 1; } _coreCmdImpl_respond(corecmd, &resp, filepath); if (filepath != NULL) { qemu_free(filepath); } break; } case AUICMD_GET_LCD_DENSITY: { UICmdRespHeader resp; resp.resp_data_size = 0; resp.result = android_hw->hw_lcd_density; _coreCmdImpl_respond(corecmd, &resp, NULL); break; } default: derror("Unknown UI control command %d is received by the Core.\n", cmd_header->cmd_type); break; } }
/* I/O write */ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value) { trace_dev_state *s = (trace_dev_state *)opaque; offset -= s->base; switch (offset >> 2) { case TRACE_DEV_REG_SWITCH: // context switch, switch to pid trace_switch(value); #ifdef DEBUG printf("QEMU.trace: kernel, context switch %u\n", value); #endif break; case TRACE_DEV_REG_TGID: // save the tgid for the following fork/clone tgid = value; #ifdef DEBUG printf("QEMU.trace: kernel, tgid %u\n", value); #endif break; case TRACE_DEV_REG_FORK: // fork, fork new pid trace_fork(tgid, value); #ifdef DEBUG printf("QEMU.trace: kernel, fork %u\n", value); #endif break; case TRACE_DEV_REG_CLONE: // fork, clone new pid (i.e. thread) trace_clone(tgid, value); #ifdef DEBUG printf("QEMU.trace: kernel, clone %u\n", value); #endif break; case TRACE_DEV_REG_EXECVE_VMSTART: // execve, vstart vstart = value; break; case TRACE_DEV_REG_EXECVE_VMEND: // execve, vend vend = value; break; case TRACE_DEV_REG_EXECVE_OFFSET: // execve, offset in EXE eoff = value; break; case TRACE_DEV_REG_EXECVE_EXEPATH: // init exec, path of EXE vstrcpy(value, path, CLIENT_PAGE_SIZE); trace_init_exec(vstart, vend, eoff, path); #ifdef DEBUG printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); #endif path[0] = 0; break; case TRACE_DEV_REG_CMDLINE_LEN: // execve, process cmdline length cmdlen = value; break; case TRACE_DEV_REG_CMDLINE: // execve, process cmdline vmemcpy(value, arg, cmdlen); trace_execve(arg, cmdlen); #ifdef DEBUG { int i; for (i = 0; i < cmdlen; i ++) if (i != cmdlen - 1 && arg[i] == 0) arg[i] = ' '; printf("QEMU.trace: kernel, execve %s[%d]\n", arg, cmdlen); } #endif arg[0] = 0; break; case TRACE_DEV_REG_EXIT: // exit, exit current process with exit code trace_exit(value); #ifdef DEBUG printf("QEMU.trace: kernel, exit %x\n", value); #endif break; case TRACE_DEV_REG_NAME: // record thread name vstrcpy(value, path, CLIENT_PAGE_SIZE); // Remove the trailing newline if it exists int len = strlen(path); if (path[len - 1] == '\n') { path[len - 1] = 0; } trace_name(path); #ifdef DEBUG printf("QEMU.trace: kernel, name %s\n", path); #endif break; case TRACE_DEV_REG_MMAP_EXEPATH: // mmap, path of EXE, the others are same as execve vstrcpy(value, path, CLIENT_PAGE_SIZE); trace_mmap(vstart, vend, eoff, path); #ifdef DEBUG printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path); #endif path[0] = 0; break; case TRACE_DEV_REG_INIT_PID: // init, name the pid that starts before device registered pid = value; break; case TRACE_DEV_REG_INIT_NAME: // init, the comm of the init pid vstrcpy(value, path, CLIENT_PAGE_SIZE); trace_init_name(tgid, pid, path); #ifdef DEBUG printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path); #endif path[0] = 0; break; case TRACE_DEV_REG_DYN_SYM_ADDR: // dynamic symbol address dsaddr = value; break; case TRACE_DEV_REG_DYN_SYM: // add dynamic symbol vstrcpy(value, arg, CLIENT_PAGE_SIZE); trace_dynamic_symbol_add(dsaddr, arg); #ifdef DEBUG printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg); #endif arg[0] = 0; break; case TRACE_DEV_REG_REMOVE_ADDR: // remove dynamic symbol addr trace_dynamic_symbol_remove(value); #ifdef DEBUG printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr); #endif arg[0] = 0; break; case TRACE_DEV_REG_PRINT_STR: // print string vstrcpy(value, arg, CLIENT_PAGE_SIZE); printf("%s", arg); arg[0] = 0; break; case TRACE_DEV_REG_PRINT_NUM_DEC: // print number in decimal printf("%d", value); break; case TRACE_DEV_REG_PRINT_NUM_HEX: // print number in hexical printf("%x", value); break; case TRACE_DEV_REG_STOP_EMU: // stop the VM execution // To ensure that the number of instructions executed in this // block is correct, we pretend that there was an exception. trace_exception(0); cpu_single_env->exception_index = EXCP_HLT; cpu_single_env->halted = 1; qemu_system_shutdown_request(); cpu_loop_exit(); break; case TRACE_DEV_REG_ENABLE: // tracing enable: 0 = stop, 1 = start if (value == 1) start_tracing(); else if (value == 0) { stop_tracing(); // To ensure that the number of instructions executed in this // block is correct, we pretend that there was an exception. trace_exception(0); } break; case TRACE_DEV_REG_UNMAP_START: unmap_start = value; break; case TRACE_DEV_REG_UNMAP_END: trace_munmap(unmap_start, value); break; default: cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset); break; } }
int main(int ac, char **av) { struct timespec timediff; struct sigaction sa; struct ex_types *funcs; struct trussinfo *trussinfo; char *fname; char *signame; char **command; pid_t childpid; int c, initial_open, status; fname = NULL; initial_open = 1; /* Initialize the trussinfo struct */ trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo)); if (trussinfo == NULL) errx(1, "calloc() failed"); trussinfo->outfile = stderr; trussinfo->strsize = 32; trussinfo->pr_why = S_NONE; trussinfo->curthread = NULL; SLIST_INIT(&trussinfo->threadlist); while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) { switch (c) { case 'p': /* specified pid */ trussinfo->pid = atoi(optarg); /* make sure i don't trace me */ if (trussinfo->pid == getpid()) { fprintf(stderr, "attempt to grab self.\n"); exit(2); } break; case 'f': /* Follow fork()'s */ trussinfo->flags |= FOLLOWFORKS; break; case 'a': /* Print execve() argument strings. */ trussinfo->flags |= EXECVEARGS; break; case 'c': /* Count number of system calls and time. */ trussinfo->flags |= COUNTONLY; break; case 'e': /* Print execve() environment strings. */ trussinfo->flags |= EXECVEENVS; break; case 'd': /* Absolute timestamps */ trussinfo->flags |= ABSOLUTETIMESTAMPS; break; case 'D': /* Relative timestamps */ trussinfo->flags |= RELATIVETIMESTAMPS; break; case 'o': /* Specified output file */ fname = optarg; break; case 's': /* Specified string size */ trussinfo->strsize = atoi(optarg); break; case 'S': /* Don't trace signals */ trussinfo->flags |= NOSIGS; break; default: usage(); } } ac -= optind; av += optind; if ((trussinfo->pid == 0 && ac == 0) || (trussinfo->pid != 0 && ac != 0)) usage(); if (fname != NULL) { /* Use output file */ /* * Set close-on-exec ('e'), so that the output file is not * shared with the traced process. */ if ((trussinfo->outfile = fopen(fname, "we")) == NULL) err(1, "cannot open %s", fname); } /* * If truss starts the process itself, it will ignore some signals -- * they should be passed off to the process, which may or may not * exit. If, however, we are examining an already-running process, * then we restore the event mask on these same signals. */ if (trussinfo->pid == 0) { /* Start a command ourselves */ command = av; trussinfo->pid = setup_and_wait(command); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); } else { sa.sa_handler = restore_proc; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); start_tracing(trussinfo->pid); } /* * At this point, if we started the process, it is stopped waiting to * be woken up, either in exit() or in execve(). */ START_TRACE: funcs = set_etype(trussinfo); initial_open = 0; /* * At this point, it's a simple loop, waiting for the process to * stop, finding out why, printing out why, and then continuing it. * All of the grunt work is done in the support routines. */ clock_gettime(CLOCK_REALTIME, &trussinfo->start_time); do { waitevent(trussinfo); switch (trussinfo->pr_why) { case S_SCE: funcs->enter_syscall(trussinfo, MAXARGS); clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->before); break; case S_SCX: clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after); if (trussinfo->curthread->in_fork && (trussinfo->flags & FOLLOWFORKS)) { trussinfo->curthread->in_fork = 0; childpid = funcs->exit_syscall(trussinfo, trussinfo->pr_data); /* * Fork a new copy of ourself to trace * the child of the original traced * process. */ if (fork() == 0) { trussinfo->pid = childpid; start_tracing(trussinfo->pid); goto START_TRACE; } break; } funcs->exit_syscall(trussinfo, MAXARGS); break; case S_SIG: if (trussinfo->flags & NOSIGS) break; if (trussinfo->flags & FOLLOWFORKS) fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); if (trussinfo->flags & ABSOLUTETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->start_time, &timediff); fprintf(trussinfo->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } if (trussinfo->flags & RELATIVETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); fprintf(trussinfo->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } signame = strsig(trussinfo->pr_data); fprintf(trussinfo->outfile, "SIGNAL %u (%s)\n", trussinfo->pr_data, signame == NULL ? "?" : signame); break; case S_EXIT: if (trussinfo->flags & COUNTONLY) break; if (trussinfo->flags & FOLLOWFORKS) fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid); if (trussinfo->flags & ABSOLUTETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->start_time, &timediff); fprintf(trussinfo->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } if (trussinfo->flags & RELATIVETIMESTAMPS) { timespecsubt(&trussinfo->curthread->after, &trussinfo->curthread->before, &timediff); fprintf(trussinfo->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec, timediff.tv_nsec); } fprintf(trussinfo->outfile, "process exit, rval = %u\n", trussinfo->pr_data); break; default: break; } } while (trussinfo->pr_why != S_EXIT && trussinfo->pr_why != S_DETACHED); if (trussinfo->flags & FOLLOWFORKS) { do { childpid = wait(&status); } while (childpid != -1); } if (trussinfo->flags & COUNTONLY) print_summary(trussinfo); fflush(trussinfo->outfile); return (0); }
static int run_master(coreid_t mycore, int argc, char *argv[]) { errval_t err; int num_spawn = strtol(argv[1], NULL, 10); int first_core = mycore + 1; debug_printf("spawning on %d cores\n", num_spawn); #ifdef TRACING err = init_tracing(); if (err_is_fail(err)) { DEBUG_ERR(err, "initialising tracing"); return EXIT_FAILURE; } // start_tracing(); prepare_dump(); #endif trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_STARTED, 0); // spawn some dispatchers char *path = argv[0]; // reuse argv and path argv[1] = NULL; for (int i = first_core; i <= num_spawn; i++) { err = spawn_program(i, path, argv, NULL, 0, NULL); if (err_is_fail(err)) { DEBUG_ERR(err, "spawning on core %d", i); } else { //debug_printf("dispatcher %d on core %d spawned\n", i, i); } } trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_WAIT, 0); // debug_printf("waiting for all spawns to start\n"); err = ns_barrier_master(first_core, num_spawn, "mem_bench_ready"); if (err_is_fail(err)) { DEBUG_ERR(err, "failed barrier_master"); return EXIT_FAILURE; } start_tracing(); trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_RUN, 0); // run_benchmark(mycore, MAX_REQUESTS_0); trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_WAIT, 0); // debug_printf("waiting for all spawns to complete\n"); err = ns_barrier_master(first_core, num_spawn, "mem_bench_finished"); if (err_is_fail(err)) { DEBUG_ERR(err, "failed barrier_master"); return EXIT_FAILURE; } trace_event(TRACE_SUBSYS_MEMTEST, TRACE_EVENT_MEMTEST_DONE, 0); debug_printf("all benchmarks completed\n"); #ifdef TRACING stop_tracing(); // dump_trace(); #endif return EXIT_SUCCESS; }
static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value) { trace_dev_state *s = (trace_dev_state *)opaque; (void)s; switch (offset >> 2) { case TRACE_DEV_REG_SWITCH: DPID("QEMU.trace: context switch tid=%u\n", value); if (trace_filename != NULL) { trace_switch(value); D("QEMU.trace: kernel, context switch %u\n", value); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_switch(value); } #endif tid = (unsigned) value; break; case TRACE_DEV_REG_TGID: DPID("QEMU.trace: tgid=%u\n", value); tgid = value; if (trace_filename != NULL) { D("QEMU.trace: kernel, tgid %u\n", value); } break; case TRACE_DEV_REG_FORK: DPID("QEMU.trace: fork (pid=%d tgid=%d value=%d)\n", pid, tgid, value); if (trace_filename != NULL) { trace_fork(tgid, value); D("QEMU.trace: kernel, fork %u\n", value); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_fork(tgid, value); } #endif break; case TRACE_DEV_REG_CLONE: DPID("QEMU.trace: clone (pid=%d tgid=%d value=%d)\n", pid, tgid, value); if (trace_filename != NULL) { trace_clone(tgid, value); D("QEMU.trace: kernel, clone %u\n", value); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_clone(tgid, value); } #endif break; case TRACE_DEV_REG_EXECVE_VMSTART: vstart = value; break; case TRACE_DEV_REG_EXECVE_VMEND: vend = value; break; case TRACE_DEV_REG_EXECVE_OFFSET: eoff = value; break; case TRACE_DEV_REG_EXECVE_EXEPATH: vstrcpy(value, exec_path, CLIENT_PAGE_SIZE); if (trace_filename != NULL) { trace_init_exec(vstart, vend, eoff, exec_path); D("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, exec_path); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { if (exec_path[0] == '\0') { memcheck_get_guest_kernel_string(exec_path, value, CLIENT_PAGE_SIZE); } memcheck_mmap_exepath(vstart, vend, eoff, exec_path); } #endif exec_path[0] = 0; break; case TRACE_DEV_REG_CMDLINE_LEN: cmdlen = value; break; case TRACE_DEV_REG_CMDLINE: safe_memory_rw_debug(cpu_single_env, value, (uint8_t*)exec_arg, cmdlen, 0); if (trace_filename != NULL) { trace_execve(exec_arg, cmdlen); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_set_cmd_line(exec_arg, cmdlen); } #endif #if DEBUG || DEBUG_PID if (trace_filename != NULL) { int i; for (i = 0; i < cmdlen; i ++) if (i != cmdlen - 1 && exec_arg[i] == 0) exec_arg[i] = ' '; printf("QEMU.trace: kernel, execve %s[%d]\n", exec_arg, cmdlen); exec_arg[0] = 0; } #endif break; case TRACE_DEV_REG_EXIT: DPID("QEMU.trace: exit tid=%u\n", value); if (trace_filename != NULL) { trace_exit(value); D("QEMU.trace: kernel, exit %x\n", value); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_exit(value); } #endif break; case TRACE_DEV_REG_NAME: vstrcpy(value, exec_path, CLIENT_PAGE_SIZE); DPID("QEMU.trace: thread name=%s\n", exec_path); int len = strlen(exec_path); if (exec_path[len - 1] == '\n') { exec_path[len - 1] = 0; } if (trace_filename != NULL) { trace_name(exec_path); D("QEMU.trace: kernel, name %s\n", exec_path); } break; case TRACE_DEV_REG_MMAP_EXEPATH: vstrcpy(value, exec_path, CLIENT_PAGE_SIZE); DPID("QEMU.trace: mmap exe=%s\n", exec_path); if (trace_filename != NULL) { trace_mmap(vstart, vend, eoff, exec_path); D("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, exec_path); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { if (exec_path[0] == '\0') { memcheck_get_guest_kernel_string(exec_path, value, CLIENT_PAGE_SIZE); } memcheck_mmap_exepath(vstart, vend, eoff, exec_path); } #endif exec_path[0] = 0; break; case TRACE_DEV_REG_INIT_PID: pid = value; DPID("QEMU.trace: pid=%d\n", value); #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_init_pid(value); } #endif break; case TRACE_DEV_REG_INIT_NAME: vstrcpy(value, exec_path, CLIENT_PAGE_SIZE); DPID("QEMU.trace: tgid=%d pid=%d name=%s\n", tgid, pid, exec_path); if (trace_filename != NULL) { trace_init_name(tgid, pid, exec_path); D("QEMU.trace: kernel, init name %u [%s]\n", pid, exec_path); } exec_path[0] = 0; break; case TRACE_DEV_REG_DYN_SYM_ADDR: dsaddr = value; break; case TRACE_DEV_REG_DYN_SYM: vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE); if (trace_filename != NULL) { trace_dynamic_symbol_add(dsaddr, exec_arg); D("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, exec_arg); } exec_arg[0] = 0; break; case TRACE_DEV_REG_REMOVE_ADDR: if (trace_filename != NULL) { trace_dynamic_symbol_remove(value); D("QEMU.trace: dynamic symbol remove %lx\n", dsaddr); } break; case TRACE_DEV_REG_PRINT_STR: vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE); printf("%s", exec_arg); exec_arg[0] = 0; break; case TRACE_DEV_REG_PRINT_NUM_DEC: printf("%d", value); break; case TRACE_DEV_REG_PRINT_NUM_HEX: printf("%x", value); break; case TRACE_DEV_REG_STOP_EMU: if (trace_filename != NULL) { trace_exception(0); } cpu_single_env->exception_index = EXCP_HLT; cpu_single_env->halted = 1; qemu_system_shutdown_request(); cpu_loop_exit(); break; case TRACE_DEV_REG_ENABLE: if (value == 1) { if (trace_filename != NULL) { start_tracing(); } } else if (value == 0) { if (trace_filename != NULL) { stop_tracing(); trace_exception(0); } } break; case TRACE_DEV_REG_UNMAP_START: unmap_start = value; break; case TRACE_DEV_REG_UNMAP_END: if (trace_filename != NULL) { trace_munmap(unmap_start, value); } #ifdef CONFIG_MEMCHECK if (memcheck_enabled) { memcheck_unmap(unmap_start, value); } #endif break; case TRACE_DEV_REG_METHOD_ENTRY: case TRACE_DEV_REG_METHOD_EXIT: case TRACE_DEV_REG_METHOD_EXCEPTION: case TRACE_DEV_REG_NATIVE_ENTRY: case TRACE_DEV_REG_NATIVE_EXIT: case TRACE_DEV_REG_NATIVE_EXCEPTION: if (trace_filename != NULL) { if (tracing) { int call_type = (offset - 4096) >> 2; trace_interpreted_method(value, call_type); } } break; #ifdef CONFIG_MEMCHECK case TRACE_DEV_REG_MALLOC: if (memcheck_enabled) { memcheck_guest_alloc(value); } break; case TRACE_DEV_REG_FREE_PTR: if (memcheck_enabled) { memcheck_guest_free(value); } break; case TRACE_DEV_REG_QUERY_MALLOC: if (memcheck_enabled) { memcheck_guest_query_malloc(value); } break; case TRACE_DEV_REG_LIBC_INIT: if (memcheck_enabled) { memcheck_guest_libc_initialized(value); } break; case TRACE_DEV_REG_PRINT_USER_STR: if (memcheck_enabled) { memcheck_guest_print_str(value); } break; #endif default: if (offset < 4096) { cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset); } else { D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset, offset, value, value); } break; } }