void gdk_profiler_start (int fd) { if (writer) return; sp_clock_init (); if (fd == -1) { gchar *filename; filename = g_strdup_printf ("gtk.%d.syscap", getpid ()); g_print ("Writing profiling data to %s\n", filename); writer = sp_capture_writer_new (filename, 16*1024); g_free (filename); } else if (fd > 2) writer = sp_capture_writer_new_from_fd (fd, 16*1024); if (writer) running = TRUE; atexit (profiler_stop); }
/** * gjs_profiler_start: * @self: A #GjsProfiler * * As expected, this starts the GjsProfiler. * * This will enable the underlying JS profiler and register a POSIX timer to * deliver SIGPROF on the configured sampling frequency. * * To reduce sampling overhead, #GjsProfiler stashes information about the * profile to be calculated once the profiler has been disabled. Calling * gjs_profiler_stop() will result in that delayed work to be completed. * * You should call gjs_profiler_stop() when the profiler is no longer needed. */ void gjs_profiler_start(GjsProfiler *self) { g_return_if_fail(self); if (self->running) return; #ifdef ENABLE_PROFILER g_return_if_fail(!self->capture); struct sigaction sa = { 0 }; struct sigevent sev = { 0 }; struct itimerspec its = { 0 }; struct itimerspec old_its; GjsAutoChar path = g_strdup(self->filename); if (!path) path = g_strdup_printf("gjs-%jd.syscap", intmax_t(self->pid)); self->capture = sp_capture_writer_new(path, 0); if (!self->capture) { g_warning("Failed to open profile capture"); return; } if (!gjs_profiler_extract_maps(self)) { g_warning("Failed to extract proc maps"); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } self->stack_depth = 0; /* Setup our signal handler for SIGPROF delivery */ sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_sigaction = gjs_profiler_sigprof; sigemptyset(&sa.sa_mask); if (sigaction(SIGPROF, &sa, nullptr) == -1) { g_warning("Failed to register sigaction handler: %s", g_strerror(errno)); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } /* * Create our SIGPROF timer * * We want to receive a SIGPROF signal on the JS thread using our * configured sampling frequency. Instead of allowing any thread to be * notified, we set the _tid value to ensure that only our thread gets * delivery of the signal. This feature is generally just for * threading implementations, but it works for us as well and ensures * that the thread is blocked while we capture the stack. */ sev.sigev_notify = SIGEV_THREAD_ID; sev.sigev_signo = SIGPROF; sev._sigev_un._tid = syscall(__NR_gettid); if (timer_create(CLOCK_MONOTONIC, &sev, &self->timer) == -1) { g_warning("Failed to create profiler timer: %s", g_strerror(errno)); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } /* Calculate sampling interval */ its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC; its.it_value.tv_sec = 0; its.it_value.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC; /* Now start this timer */ if (timer_settime(self->timer, 0, &its, &old_its) != 0) { g_warning("Failed to enable profiler timer: %s", g_strerror(errno)); timer_delete(self->timer); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } self->running = true; /* Notify the JS runtime of where to put stack info */ js::SetContextProfilingStack(self->cx, self->stack, &self->stack_depth, G_N_ELEMENTS(self->stack)); /* Start recording stack info */ js::EnableContextProfilingStack(self->cx, true); g_message("Profiler started"); #else /* !ENABLE_PROFILER */ self->running = true; g_message("Profiler is disabled. Recompile with --enable-profiler to use."); #endif /* ENABLE_PROFILER */ }