summaryrefslogtreecommitdiff
path: root/gjs
diff options
context:
space:
mode:
authorPhilip Chimento <philip.chimento@gmail.com>2019-06-07 20:32:56 +0000
committerPhilip Chimento <philip.chimento@gmail.com>2019-06-07 20:32:56 +0000
commit9bdbf2f297ba899a1329cac9b6f7c0e4c79fbfff (patch)
treec503c0bcd1f7f107bd73cecca7730e474970c295 /gjs
parent4eb8a82aff41d5ce75d1a22b3041911bf7f0c6b2 (diff)
parent0218b1232da179adf230b5bd09dcfdc2baf7f1d5 (diff)
downloadgjs-9bdbf2f297ba899a1329cac9b6f7c0e4c79fbfff.tar.gz
Merge branch 'wip/chergert/sysprof-3' into 'master'
Add support for GJS_TRACE_FD See merge request GNOME/gjs!295
Diffstat (limited to 'gjs')
-rw-r--r--gjs/console.cpp20
-rw-r--r--gjs/context-private.h5
-rw-r--r--gjs/context.cpp20
-rw-r--r--gjs/profiler-private.h4
-rw-r--r--gjs/profiler.cpp89
-rw-r--r--gjs/profiler.h2
6 files changed, 120 insertions, 20 deletions
diff --git a/gjs/console.cpp b/gjs/console.cpp
index 4f876153..2f813c7c 100644
--- a/gjs/console.cpp
+++ b/gjs/console.cpp
@@ -281,10 +281,21 @@ main(int argc, char **argv)
/* This should be removed after a suitable time has passed */
check_script_args_for_stray_gjs_args(script_argc, script_argv);
+ /* Check for GJS_TRACE_FD for sysprof profiling */
+ const char* env_tracefd = g_getenv("GJS_TRACE_FD");
+ int tracefd = -1;
+ if (env_tracefd) {
+ tracefd = g_ascii_strtoll(env_tracefd, nullptr, 10);
+ g_setenv("GJS_TRACE_FD", "", true);
+ if (tracefd > 0)
+ enable_profiler = true;
+ }
+
if (interactive_mode && enable_profiler) {
g_message("Profiler disabled in interactive mode.");
enable_profiler = false;
g_unsetenv("GJS_ENABLE_PROFILER"); /* ignore env var in eval() */
+ g_unsetenv("GJS_TRACE_FD"); /* ignore env var in eval() */
}
js_context = (GjsContext*) g_object_new(GJS_TYPE_CONTEXT,
@@ -318,6 +329,15 @@ main(int argc, char **argv)
if (enable_profiler && profile_output_path) {
GjsProfiler *profiler = gjs_context_get_profiler(js_context);
gjs_profiler_set_filename(profiler, profile_output_path);
+ } else if (enable_profiler && tracefd > -1) {
+ GjsProfiler* profiler = gjs_context_get_profiler(js_context);
+ gjs_profiler_set_fd(profiler, tracefd);
+ tracefd = -1;
+ }
+
+ if (tracefd != -1) {
+ close(tracefd);
+ tracefd = -1;
}
/* prepare command line arguments */
diff --git a/gjs/context-private.h b/gjs/context-private.h
index 10dcf688..274352fa 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -117,6 +117,8 @@ class GjsContextPrivate {
bool m_should_profile : 1;
bool m_should_listen_sigusr2 : 1;
+ int64_t m_sweep_begin_time;
+
void schedule_gc_internal(bool force_gc);
static gboolean trigger_gc_if_needed(void* data);
static gboolean drain_job_queue_idle_handler(void* data);
@@ -148,7 +150,6 @@ class GjsContextPrivate {
GJS_USE const GjsAtoms& atoms(void) const { return m_atoms; }
GJS_USE bool destroying(void) const { return m_destroying; }
GJS_USE bool sweeping(void) const { return m_in_gc_sweep; }
- void set_sweeping(bool value) { m_in_gc_sweep = value; }
GJS_USE const char* program_name(void) const { return m_program_name; }
void set_program_name(char* value) { m_program_name = value; }
void set_search_path(char** value) { m_search_path = value; }
@@ -194,6 +195,8 @@ class GjsContextPrivate {
void register_unhandled_promise_rejection(uint64_t id, GjsAutoChar&& stack);
void unregister_unhandled_promise_rejection(uint64_t id);
+ void set_sweeping(bool value);
+
static void trace(JSTracer* trc, void* data);
void free_profiler(void);
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 0ac7fb68..6e2d54af 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -609,6 +609,26 @@ void GjsContextPrivate::schedule_gc_if_needed(void) {
schedule_gc_internal(false);
}
+void GjsContextPrivate::set_sweeping(bool value) {
+ // If we have a profiler enabled, record the duration of GC sweep
+ if (this->m_profiler != nullptr) {
+ int64_t now = g_get_monotonic_time() * 1000L;
+
+ if (value) {
+ m_sweep_begin_time = now;
+ } else {
+ if (m_sweep_begin_time != 0) {
+ _gjs_profiler_add_mark(this->m_profiler, m_sweep_begin_time,
+ now - m_sweep_begin_time, "GJS", "Sweep",
+ nullptr);
+ m_sweep_begin_time = 0;
+ }
+ }
+ }
+
+ m_in_gc_sweep = value;
+}
+
void GjsContextPrivate::exit(uint8_t exit_code) {
g_assert(!m_should_exit);
m_should_exit = true;
diff --git a/gjs/profiler-private.h b/gjs/profiler-private.h
index 20cb9f46..0697be25 100644
--- a/gjs/profiler-private.h
+++ b/gjs/profiler-private.h
@@ -33,6 +33,10 @@ G_BEGIN_DECLS
GjsProfiler *_gjs_profiler_new(GjsContext *context);
void _gjs_profiler_free(GjsProfiler *self);
+void _gjs_profiler_add_mark(GjsProfiler* self, gint64 time, gint64 duration,
+ const char* group, const char* name,
+ const char* message);
+
GJS_USE
bool _gjs_profiler_is_running(GjsProfiler *self);
diff --git a/gjs/profiler.cpp b/gjs/profiler.cpp
index d7ee03b5..55aa8916 100644
--- a/gjs/profiler.cpp
+++ b/gjs/profiler.cpp
@@ -47,9 +47,11 @@
# ifdef G_OS_UNIX
# include <glib-unix.h>
# endif
-# include "util/sp-capture-writer.h"
+# include <sysprof-capture.h>
#endif
+#define FLUSH_DELAY_SECONDS 3
+
/*
* This is mostly non-exciting code wrapping the builtin Profiler in
* mozjs. In particular, the profiler consumer is required to "bring your
@@ -95,12 +97,15 @@ struct _GjsProfiler {
JSContext *cx;
/* Buffers and writes our sampled stacks */
- SpCaptureWriter *capture;
+ SysprofCaptureWriter* capture;
#endif /* ENABLE_PROFILER */
/* The filename to write to */
char *filename;
+ /* An FD to capture to */
+ int fd;
+
#ifdef ENABLE_PROFILER
/* Our POSIX timer to wakeup SIGPROF */
timer_t timer;
@@ -166,8 +171,8 @@ gjs_profiler_extract_maps(GjsProfiler *self)
inode = 0;
}
- if (!sp_capture_writer_add_map(self->capture, now, -1, self->pid, start,
- end, offset, inode, file))
+ if (!sysprof_capture_writer_add_map(self->capture, now, -1, self->pid,
+ start, end, offset, inode, file))
return false;
}
@@ -219,6 +224,7 @@ _gjs_profiler_new(GjsContext *context)
self->cx = static_cast<JSContext *>(gjs_context_get_native_context(context));
self->pid = getpid();
#endif
+ self->fd = -1;
profiling_context = context;
@@ -247,7 +253,10 @@ _gjs_profiler_free(GjsProfiler *self)
g_clear_pointer(&self->filename, g_free);
#ifdef ENABLE_PROFILER
- g_clear_pointer(&self->capture, sp_capture_writer_unref);
+ g_clear_pointer(&self->capture, sysprof_capture_writer_unref);
+
+ if (self->fd != -1)
+ close(self->fd);
#endif
g_free(self);
}
@@ -304,7 +313,8 @@ gjs_profiler_sigprof(int signum,
* here since we are in a signal handler.
*/
// cppcheck-suppress allocaCalled
- SpCaptureAddress *addrs = static_cast<SpCaptureAddress *>(alloca(sizeof *addrs * depth));
+ SysprofCaptureAddress* addrs =
+ static_cast<SysprofCaptureAddress*>(alloca(sizeof *addrs * depth));
for (uint32_t ix = 0; ix < depth; ix++) {
js::ProfileEntry& entry = self->stack.entries[ix];
@@ -360,12 +370,14 @@ gjs_profiler_sigprof(int signum,
* everything will show up as [stack] when building callgraphs.
*/
if (final_string[0] != '\0')
- addrs[flipped] = sp_capture_writer_add_jitmap(self->capture, final_string);
+ addrs[flipped] =
+ sysprof_capture_writer_add_jitmap(self->capture, final_string);
else
- addrs[flipped] = SpCaptureAddress(entry.stackAddress());
+ addrs[flipped] = SysprofCaptureAddress(entry.stackAddress());
}
- if (!sp_capture_writer_add_sample(self->capture, now, -1, self->pid, addrs, depth))
+ if (!sysprof_capture_writer_add_sample(self->capture, now, -1, self->pid,
+ -1, addrs, depth))
gjs_profiler_stop(self);
}
@@ -402,20 +414,30 @@ gjs_profiler_start(GjsProfiler *self)
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));
+ if (self->fd != -1) {
+ self->capture = sysprof_capture_writer_new_from_fd(self->fd, 0);
+ self->fd = -1;
+ } else {
+ 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);
+ self->capture = sysprof_capture_writer_new(path, 0);
+ }
if (!self->capture) {
g_warning("Failed to open profile capture");
return;
}
+ /* Automatically flush to be resilient against SIGINT, etc */
+ sysprof_capture_writer_set_flush_delay(self->capture,
+ g_main_context_get_thread_default(),
+ FLUSH_DELAY_SECONDS);
+
if (!gjs_profiler_extract_maps(self)) {
g_warning("Failed to extract proc maps");
- g_clear_pointer(&self->capture, sp_capture_writer_unref);
+ g_clear_pointer(&self->capture, sysprof_capture_writer_unref);
return;
}
@@ -426,7 +448,7 @@ gjs_profiler_start(GjsProfiler *self)
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);
+ g_clear_pointer(&self->capture, sysprof_capture_writer_unref);
return;
}
@@ -446,7 +468,7 @@ gjs_profiler_start(GjsProfiler *self)
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);
+ g_clear_pointer(&self->capture, sysprof_capture_writer_unref);
return;
}
@@ -460,7 +482,7 @@ gjs_profiler_start(GjsProfiler *self)
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);
+ g_clear_pointer(&self->capture, sysprof_capture_writer_unref);
return;
}
@@ -515,9 +537,9 @@ gjs_profiler_stop(GjsProfiler *self)
js::EnableContextProfilingStack(self->cx, false);
js::SetContextProfilingStack(self->cx, nullptr);
- sp_capture_writer_flush(self->capture);
+ sysprof_capture_writer_flush(self->capture);
- g_clear_pointer(&self->capture, sp_capture_writer_unref);
+ g_clear_pointer(&self->capture, sysprof_capture_writer_unref);
g_message("Profiler stopped");
@@ -639,3 +661,32 @@ gjs_profiler_set_filename(GjsProfiler *self,
g_free(self->filename);
self->filename = g_strdup(filename);
}
+
+void _gjs_profiler_add_mark(GjsProfiler* self, gint64 time_nsec,
+ gint64 duration_nsec, const char* group,
+ const char* name, const char* message) {
+ g_return_if_fail(self);
+ g_return_if_fail(group);
+ g_return_if_fail(name);
+
+#ifdef ENABLE_PROFILER
+ if (self->running && self->capture != nullptr) {
+ sysprof_capture_writer_add_mark(self->capture, time_nsec, -1, self->pid,
+ duration_nsec, group, name, message);
+ }
+#endif
+}
+
+void gjs_profiler_set_fd(GjsProfiler* self, int fd) {
+ g_return_if_fail(self);
+ g_return_if_fail(!self->filename);
+ g_return_if_fail(!self->running);
+
+#ifdef ENABLE_PROFILER
+ if (self->fd != fd) {
+ if (self->fd != -1)
+ close(self->fd);
+ self->fd = fd;
+ }
+#endif
+}
diff --git a/gjs/profiler.h b/gjs/profiler.h
index 8bebed25..95615bd4 100644
--- a/gjs/profiler.h
+++ b/gjs/profiler.h
@@ -40,6 +40,8 @@ GType gjs_profiler_get_type(void);
GJS_EXPORT
void gjs_profiler_set_filename(GjsProfiler *self,
const char *filename);
+GJS_EXPORT
+void gjs_profiler_set_fd(GjsProfiler* self, int fd);
GJS_EXPORT
void gjs_profiler_start(GjsProfiler *self);