Commit 9424ffa2 authored by Philip Chimento's avatar Philip Chimento 🚮 Committed by Philip Chimento

profiler: Allow configuring with --disable-profiler

We check for the existence of timer_settime() with some M4 macros from
gnulib. These POSIX APIs are required for building the profiler.

Allow disabling the profiler with a configure switch. This keeps the API
(and its preconditions as much as possible) but makes it a no-op and
prints an informational message at runtime on attempted use. We also
leave out the sysprof writer code and skip the profiler tests if the
profiler is not being built.
parent 12b5a8f9
......@@ -77,6 +77,10 @@ endif
# reasons
libgjs_la_SOURCES = $(gjs_srcs)
if ENABLE_PROFILER
libgjs_la_SOURCES += $(gjs_sysprof_srcs)
endif
# Also, these files used to be a separate library
libgjs_private_source_files = $(gjs_private_srcs)
......
......@@ -30,6 +30,7 @@ PKG_PROG_PKG_CONFIG
PKG_INSTALLDIR
AC_LANG([C++])
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX_11
......@@ -105,6 +106,18 @@ AS_IF([test x$have_gtk = xyes], [
], [AS_IF([test "x$with_gtk" = "xyes"],
[AC_MSG_ERROR([GTK requested but not found])])])
# Some Linux APIs required for profiler
AC_ARG_ENABLE([profiler],
[AS_HELP_STRING([--disable-profiler], [Don't build profiler])])
AS_IF([test x$enable_profiler != xno], [
gl_TIMER_TIME
AS_IF([test x$ac_cv_func_timer_settime = xno],
[AC_MSG_ERROR([The profiler is currently only supported on Linux.
Configure with --disable-profiler to skip it on other platforms.])])
AC_DEFINE([ENABLE_PROFILER], [1], [Define if the profiler should be built.])
])
AM_CONDITIONAL([ENABLE_PROFILER], [test x$enable_profiler != xno])
PKG_CHECK_VAR([GI_DATADIR], [gobject-introspection-1.0], [gidatadir])
# readline
......@@ -309,6 +322,7 @@ AC_MSG_RESULT([
readline: ${ac_cv_header_readline_readline_h}
dtrace: ${enable_dtrace:-no}
systemtap: ${enable_systemtap:-no}
Profiler: ${enable_profiler:-yes}
Run tests under: ${TEST_MSG}
Code coverage: ${enable_code_coverage}
])
......@@ -86,9 +86,6 @@ gjs_srcs = \
util/log.h \
util/misc.cpp \
util/misc.h \
util/sp-capture-types.h \
util/sp-capture-writer.c \
util/sp-capture-writer.h \
$(NULL)
# These files were part of a separate library
......@@ -107,3 +104,9 @@ gjs_gtk_private_srcs = \
gjs_console_srcs = \
gjs/console.cpp \
$(NULL)
gjs_sysprof_srcs = \
util/sp-capture-types.h \
util/sp-capture-writer.c \
util/sp-capture-writer.h \
$(NULL)
......@@ -39,7 +39,9 @@
#include "jsapi-util.h"
#include "profiler.h"
#include "util/sp-capture-writer.h"
#ifdef ENABLE_PROFILER
# include "util/sp-capture-writer.h"
#endif
/*
* This is mostly non-exciting code wrapping the builtin Profiler in
......@@ -75,6 +77,7 @@
G_DEFINE_POINTER_TYPE(GjsProfiler, gjs_profiler)
struct _GjsProfiler {
#ifdef ENABLE_PROFILER
/* The stack for the JSContext profiler to use for current stack
* information while executing. We will look into this during our
* SIGPROF handler.
......@@ -86,10 +89,12 @@ struct _GjsProfiler {
/* Buffers and writes our sampled stacks */
SpCaptureWriter *capture;
#endif /* ENABLE_PROFILER */
/* The filename to write to */
char *filename;
#ifdef ENABLE_PROFILER
/* Our POSIX timer to wakeup SIGPROF */
timer_t timer;
......@@ -101,6 +106,7 @@ struct _GjsProfiler {
/* Cached copy of our pid */
GPid pid;
#endif /* ENABLE_PROFILER */
/* If we are currently sampling */
unsigned running : 1;
......@@ -108,6 +114,7 @@ struct _GjsProfiler {
static GjsProfiler *current_profiler;
#ifdef ENABLE_PROFILER
/*
* gjs_profiler_extract_maps:
*
......@@ -163,6 +170,7 @@ gjs_profiler_extract_maps(GjsProfiler *self)
return true;
}
#endif /* ENABLE_PROFILER */
/**
* gjs_profiler_new:
......@@ -191,12 +199,13 @@ gjs_profiler_new(GjsContext *context)
g_assert(((void)"You can ony create one profiler at a time.",
!current_profiler));
auto cx = static_cast<JSContext *>(gjs_context_get_native_context(context));
GjsProfiler *self = g_new0(GjsProfiler, 1);
self->cx = cx;
#ifdef ENABLE_PROFILER
self->cx = static_cast<JSContext *>(gjs_context_get_native_context(context));
self->pid = getpid();
#endif
current_profiler = self;
......@@ -224,7 +233,9 @@ gjs_profiler_free(GjsProfiler *self)
current_profiler = nullptr;
g_clear_pointer(&self->filename, g_free);
#ifdef ENABLE_PROFILER
g_clear_pointer(&self->capture, sp_capture_writer_unref);
#endif
g_free(self);
}
......@@ -245,6 +256,8 @@ gjs_profiler_is_running(GjsProfiler *self)
return self->running;
}
#ifdef ENABLE_PROFILER
/* Run from a signal handler */
static inline unsigned
gjs_profiler_get_stack_size(GjsProfiler *self)
......@@ -322,6 +335,8 @@ gjs_profiler_sigprof(int signum,
gjs_profiler_stop(self);
}
#endif /* ENABLE_PROFILER */
/**
* gjs_profiler_start:
* @self: A #GjsProfiler
......@@ -341,6 +356,11 @@ 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 }};
......@@ -348,9 +368,6 @@ gjs_profiler_start(GjsProfiler *self)
struct itimerspec its = {{ 0 }};
struct itimerspec old_its;
if (self->running)
return;
GjsAutoChar path = g_strdup(self->filename);
if (!path)
path = g_strdup_printf("gjs-%jd.syscap", intmax_t(self->pid));
......@@ -425,6 +442,13 @@ gjs_profiler_start(GjsProfiler *self)
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 */
}
/**
......@@ -446,13 +470,14 @@ gjs_profiler_stop(GjsProfiler *self)
{
/* Note: can be called from a signal handler */
struct itimerspec its = {{ 0 }};
g_assert(self);
if (!self->running)
return;
#ifdef ENABLE_PROFILER
struct itimerspec its = {{ 0 }};
timer_settime(self->timer, 0, &its, nullptr);
timer_delete(self->timer);
......@@ -464,9 +489,11 @@ gjs_profiler_stop(GjsProfiler *self)
g_clear_pointer(&self->capture, sp_capture_writer_unref);
self->stack_depth = 0;
self->running = false;
g_message("Profiler stopped");
#endif /* ENABLE_PROFILER */
self->running = false;
}
static gboolean
......@@ -497,12 +524,20 @@ gjs_profiler_sigusr2(void *unused)
void
gjs_profiler_setup_signals(void)
{
#ifdef ENABLE_PROFILER
static bool initialized = false;
if (!initialized) {
initialized = true;
g_unix_signal_add(SIGUSR2, gjs_profiler_sigusr2, nullptr);
}
#else /* !ENABLE_PROFILER */
g_message("Profiler is disabled. Not setting up signals.");
#endif /* ENABLE_PROFILER */
}
/**
......@@ -520,6 +555,8 @@ gjs_profiler_setup_signals(void)
bool
gjs_profiler_chain_signal(siginfo_t *info)
{
#ifdef ENABLE_PROFILER
if (info) {
if (info->si_signo == SIGPROF) {
gjs_profiler_sigprof(SIGPROF, info, nullptr);
......@@ -532,6 +569,8 @@ gjs_profiler_chain_signal(siginfo_t *info)
}
}
#endif /* ENABLE_PROFILER */
return false;
}
......
......@@ -75,6 +75,11 @@ report_xfail () {
fi
}
skip () {
total=$((total + 1))
echo "ok $total - $1 # SKIP $2"
}
# Test that System.exit() works in gjs-console
$gjs -c 'imports.system.exit(0)'
report "System.exit(0) should exit successfully"
......@@ -159,14 +164,23 @@ report "--version after -c should not print anything"
rm -f gjs-*.syscap foo.syscap
$gjs -c 'imports.system.exit(0)' && test ! -f gjs-*.syscap
report "no profiling data should be dumped without --profile"
$gjs --profile -c 'imports.system.exit(0)' && test -f gjs-*.syscap
report "--profile should dump profiling data to the default file name"
$gjs --profile=foo.syscap -c 'imports.system.exit(0)' && test -f foo.syscap
report "--profile with argument should dump profiling data to the named file"
rm -f gjs-*.syscap foo.syscap
GJS_ENABLE_PROFILER=1 $gjs -c 'imports.system.exit(0)' && test -f gjs-*.syscap
report "GJS_ENABLE_PROFILER=1 should enable the profiler"
rm -f gjs-*.syscap
# Skip some tests if built without profiler support
if gjs --profile -c 1 2>&1 | grep -q 'Gjs-Message.*Profiler is disabled'; then
reason="profiler is disabled"
skip "--profile should dump profiling data to the default file name" "$reason"
skip "--profile with argument should dump profiling data to the named file" "$reason"
skip "GJS_ENABLE_PROFILER=1 should enable the profiler" "$reason"
else
$gjs --profile -c 'imports.system.exit(0)' && test -f gjs-*.syscap
report "--profile should dump profiling data to the default file name"
$gjs --profile=foo.syscap -c 'imports.system.exit(0)' && test -f foo.syscap
report "--profile with argument should dump profiling data to the named file"
rm -f gjs-*.syscap foo.syscap
GJS_ENABLE_PROFILER=1 $gjs -c 'imports.system.exit(0)' && test -f gjs-*.syscap
report "GJS_ENABLE_PROFILER=1 should enable the profiler"
rm -f gjs-*.syscap
fi
# interpreter handles queued promise jobs correctly
output=$($gjs promise.js)
......
# serial 17 -*- Autoconf -*-
# Enable extensions on systems that normally disable them.
# Copyright (C) 2003, 2006-2018 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This definition of AC_USE_SYSTEM_EXTENSIONS is stolen from git
# Autoconf. Perhaps we can remove this once we can assume Autoconf
# 2.70 or later everywhere, but since Autoconf mutates rapidly
# enough in this area it's likely we'll need to redefine
# AC_USE_SYSTEM_EXTENSIONS for quite some time.
# If autoconf reports a warning
# warning: AC_COMPILE_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS
# or warning: AC_RUN_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS
# the fix is
# 1) to ensure that AC_USE_SYSTEM_EXTENSIONS is never directly invoked
# but always AC_REQUIREd,
# 2) to ensure that for each occurrence of
# AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
# or
# AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
# the corresponding gnulib module description has 'extensions' among
# its dependencies. This will ensure that the gl_USE_SYSTEM_EXTENSIONS
# invocation occurs in gl_EARLY, not in gl_INIT.
# AC_USE_SYSTEM_EXTENSIONS
# ------------------------
# Enable extensions on systems that normally disable them,
# typically due to standards-conformance issues.
#
# Remember that #undef in AH_VERBATIM gets replaced with #define by
# AC_DEFINE. The goal here is to define all known feature-enabling
# macros, then, if reports of conflicts are made, disable macros that
# cause problems on some platforms (such as __EXTENSIONS__).
AC_DEFUN_ONCE([AC_USE_SYSTEM_EXTENSIONS],
[AC_BEFORE([$0], [AC_COMPILE_IFELSE])dnl
AC_BEFORE([$0], [AC_RUN_IFELSE])dnl
AC_CHECK_HEADER([minix/config.h], [MINIX=yes], [MINIX=])
if test "$MINIX" = yes; then
AC_DEFINE([_POSIX_SOURCE], [1],
[Define to 1 if you need to in order for 'stat' and other
things to work.])
AC_DEFINE([_POSIX_1_SOURCE], [2],
[Define to 2 if the system does not provide POSIX.1 features
except with this defined.])
AC_DEFINE([_MINIX], [1],
[Define to 1 if on MINIX.])
AC_DEFINE([_NETBSD_SOURCE], [1],
[Define to 1 to make NetBSD features available. MINIX 3 needs this.])
fi
dnl Use a different key than __EXTENSIONS__, as that name broke existing
dnl configure.ac when using autoheader 2.62.
AH_VERBATIM([USE_SYSTEM_EXTENSIONS],
[/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable general extensions on macOS. */
#ifndef _DARWIN_C_SOURCE
# undef _DARWIN_C_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable NetBSD extensions on NetBSD. */
#ifndef _NETBSD_SOURCE
# undef _NETBSD_SOURCE
#endif
/* Enable OpenBSD extensions on NetBSD. */
#ifndef _OPENBSD_SOURCE
# undef _OPENBSD_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
# undef __STDC_WANT_IEC_60559_BFP_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
# undef __STDC_WANT_IEC_60559_DFP_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
#endif
/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
# undef __STDC_WANT_IEC_60559_TYPES_EXT__
#endif
/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
#ifndef __STDC_WANT_LIB_EXT2__
# undef __STDC_WANT_LIB_EXT2__
#endif
/* Enable extensions specified by ISO/IEC 24747:2009. */
#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
# undef __STDC_WANT_MATH_SPEC_FUNCS__
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable X/Open extensions if necessary. HP-UX 11.11 defines
mbstate_t only if _XOPEN_SOURCE is defined to 500, regardless of
whether compiling with -Ae or -D_HPUX_SOURCE=1. */
#ifndef _XOPEN_SOURCE
# undef _XOPEN_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
])
AC_CACHE_CHECK([whether it is safe to define __EXTENSIONS__],
[ac_cv_safe_to_define___extensions__],
[AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[
# define __EXTENSIONS__ 1
]AC_INCLUDES_DEFAULT])],
[ac_cv_safe_to_define___extensions__=yes],
[ac_cv_safe_to_define___extensions__=no])])
test $ac_cv_safe_to_define___extensions__ = yes &&
AC_DEFINE([__EXTENSIONS__])
AC_DEFINE([_ALL_SOURCE])
AC_DEFINE([_DARWIN_C_SOURCE])
AC_DEFINE([_GNU_SOURCE])
AC_DEFINE([_NETBSD_SOURCE])
AC_DEFINE([_OPENBSD_SOURCE])
AC_DEFINE([_POSIX_PTHREAD_SEMANTICS])
AC_DEFINE([__STDC_WANT_IEC_60559_ATTRIBS_EXT__])
AC_DEFINE([__STDC_WANT_IEC_60559_BFP_EXT__])
AC_DEFINE([__STDC_WANT_IEC_60559_DFP_EXT__])
AC_DEFINE([__STDC_WANT_IEC_60559_FUNCS_EXT__])
AC_DEFINE([__STDC_WANT_IEC_60559_TYPES_EXT__])
AC_DEFINE([__STDC_WANT_LIB_EXT2__])
AC_DEFINE([__STDC_WANT_MATH_SPEC_FUNCS__])
AC_DEFINE([_TANDEM_SOURCE])
AC_CACHE_CHECK([whether _XOPEN_SOURCE should be defined],
[ac_cv_should_define__xopen_source],
[ac_cv_should_define__xopen_source=no
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[
#include <wchar.h>
mbstate_t x;]])],
[],
[AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[
#define _XOPEN_SOURCE 500
#include <wchar.h>
mbstate_t x;]])],
[ac_cv_should_define__xopen_source=yes])])])
test $ac_cv_should_define__xopen_source = yes &&
AC_DEFINE([_XOPEN_SOURCE], [500])
])# AC_USE_SYSTEM_EXTENSIONS
# gl_USE_SYSTEM_EXTENSIONS
# ------------------------
# Enable extensions on systems that normally disable them,
# typically due to standards-conformance issues.
AC_DEFUN_ONCE([gl_USE_SYSTEM_EXTENSIONS],
[
dnl Require this macro before AC_USE_SYSTEM_EXTENSIONS.
dnl gnulib does not need it. But if it gets required by third-party macros
dnl after AC_USE_SYSTEM_EXTENSIONS is required, autoconf 2.62..2.63 emit a
dnl warning: "AC_COMPILE_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS".
dnl Note: We can do this only for one of the macros AC_AIX, AC_GNU_SOURCE,
dnl AC_MINIX. If people still use AC_AIX or AC_MINIX, they are out of luck.
AC_REQUIRE([AC_GNU_SOURCE])
AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
])
# timer_time.m4 serial 3
dnl Copyright (C) 2011-2018 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
# Check for timer_settime, and set LIB_TIMER_TIME.
AC_DEFUN([gl_TIMER_TIME],
[
dnl Based on clock_time.m4. See details there.
AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
dnl Test whether the gnulib module 'threadlib' is in use.
dnl Some packages like Emacs use --avoid=threadlib.
dnl Write the symbol in such a way that it does not cause 'aclocal' to pick
dnl the threadlib.m4 file that is installed in $PREFIX/share/aclocal/.
m4_ifdef([gl_][THREADLIB], [AC_REQUIRE([gl_][THREADLIB])])
LIB_TIMER_TIME=
AC_SUBST([LIB_TIMER_TIME])
gl_saved_libs=$LIBS
AC_SEARCH_LIBS([timer_settime], [rt posix4],
[test "$ac_cv_search_timer_settime" = "none required" ||
LIB_TIMER_TIME=$ac_cv_search_timer_settime])
m4_ifdef([gl_][THREADLIB],
[dnl GLIBC uses threads to emulate posix timers when kernel support
dnl is not available (like Linux < 2.6 or when used with kFreeBSD)
dnl Now the pthread lib is linked automatically in the normal case,
dnl but when linking statically, it needs to be explicitly specified.
AC_EGREP_CPP([Thread],
[#include <features.h>
#ifdef __GNU_LIBRARY__
#if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || (__GLIBC__ > 2)) \
&& !(__UCLIBC__ && __HAS_NO_THREADS__)
Thread emulation available
#endif
#endif
],
[LIB_TIMER_TIME="$LIB_TIMER_TIME $LIBMULTITHREAD"])])
AC_CHECK_FUNCS([timer_settime])
LIBS=$gl_saved_libs
])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment