Upstream-Status: Backport

From ca6d916a6f8f0f8abbb4c9b6a97dd1a1615bb124 Mon Sep 17 00:00:00 2001
From: Maynard Johnson <maynardj@us.ibm.com>
Date: Wed, 5 Dec 2012 10:16:35 -0600
Subject: [PATCH] Fix up configure to handle architectures that do not
 implement perf_event_open

This patch fixes the following problems:

1) The configure script allows the user to pass a location to kernel
headers (via --with-kernel option) such that, even if the running
kernel does not have perf_events support, it may be possible to
build operf (e.g., in cross-compile environments).  But the message
'This kernel does not have perf_events support; falling back to legacy
oprofile' was being displayed inappropriately in such cases.  This
patch changes the configure script so that the "falling back to
legacy oprofile" message will only be displayed if we're running
on a kernel that does not have perf_events support AND the user
did not pass specify the "--with-kernel" option.

2) Some architectures don't even implement the perf_event_open syscall, so the
configure script must do more than checking kernel version and whether or not
perf_event.h is present in order to decide if perf_events is supported.
This patch provides that extra capability.

These problems were reported by Tony Jones <tonyj@suse.com>.

Signed-off-by:  Maynard Johnson <maynardj@us.ibm.com>
---
 configure.ac                   |   74 +++++++++++++++++++++++++++++-----------
 utils/op_perf_events_checker.c |    6 ++--
 2 files changed, 58 insertions(+), 22 deletions(-)

diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -70,35 +70,70 @@ KERNELDIR=$withval)
 
 
 dnl Check kernel version for perf_events supported
-AC_MSG_CHECKING([kernel version supports perf_events])
 if test "$KERNELDIR" != ""; then
 	KINC="$KERNELDIR/include"
+	PERF_EVENT_FLAGS=" -I$KERNELDIR/include"
+	AC_SUBST(PERF_EVENT_FLAGS)
+	PERF_EVENT_H="$KERNELDIR/include/linux/perf_event.h"
+else
+	PERF_EVENT_H="/usr/include/linux/perf_event.h"
 fi
-AX_KERNEL_VERSION(2, 6, 31, <=, kernel_has_perf_events_support="yes",
+
+PERF_EVENT_H_EXISTS="no"
+kernel_may_have_perf_events_support="no"
+AX_KERNEL_VERSION(2, 6, 31, <=, kernel_may_have_perf_events_support="yes",
 kernel_has_perf_events_support="no")
 
-if test "$kernel_has_perf_events_support" = "no"; then
-	AC_MSG_RESULT([This kernel does not have perf_events support; falling back to legacy oprofile])
+dnl The AX_KERNEL_VERSION macro may return kernel_may_have_perf_events_support="yes",
+dnl indicating a partial answer.  Some architectures do not implement the Performance
+dnl Events Kernel Subsystem even with kernel versions > 2.6.31 -- i.e., not even
+dnl implementing the perf_event_open syscall to return ENOSYS.  So the check below
+dnl will identify and handle such situations.
+
+if test "$kernel_may_have_perf_events_support" = "yes"; then
+	AC_CHECK_HEADER($PERF_EVENT_H,PERF_EVENT_H_EXISTS="yes")
+	AC_MSG_CHECKING([kernel supports perf_events])
+	if test "$PERF_EVENT_H_EXISTS" = "yes"; then
+		rm -f test-for-PERF_EVENT_OPEN
+		AC_LANG_CONFTEST(
+			[AC_LANG_PROGRAM([[#include <linux/perf_event.h>
+				#include <asm/unistd.h>
+				#include <sys/types.h>
+				#include <string.h>
+			]],
+			[[struct perf_event_attr attr;
+				pid_t pid;
+				memset(&attr, 0, sizeof(attr));
+				attr.size = sizeof(attr);
+				attr.sample_type = PERF_SAMPLE_IP;
+				pid = getpid();
+				syscall(__NR_perf_event_open, &attr, pid, 0, -1, 0);
+				]])
+		])
+		$CC conftest.$ac_ext $CFLAGS $LDFLAGS $LIBS $PERF_EVENT_FLAGS -o test-for-PERF_EVENT_OPEN  > /dev/null 2>&1
+		if test -f test-for-PERF_EVENT_OPEN; then
+			kernel_has_perf_events_support="yes"
+			AC_MSG_RESULT(yes)
+		else
+			AC_MSG_RESULT(no)
+			kernel_has_perf_events_support="no"
+		fi
+	else
+		AC_MSG_RESULT(unknown -- perf_event.h not found)
+	fi
 else
-	AC_MSG_RESULT([This kernel has perf_events support])
+	AC_MSG_RESULT(kernel supports perf_events... no)
+	kernel_has_perf_events_support="no"
+fi
+AM_CONDITIONAL(BUILD_FOR_PERF_EVENT, test "$kernel_has_perf_events_support" = "yes")
+
+if test "$kernel_has_perf_events_support" = "yes"; then
+	HAVE_PERF_EVENTS='1'
+else
+	HAVE_PERF_EVENTS='0'
+	AC_MSG_RESULT([No perf_events support available; falling back to legacy oprofile])
 fi
 
-if test "$KERNELDIR" == ""; then
-	PERF_EVENT_H="/usr/include/linux/perf_event.h"
-else
-	PERF_EVENT_H="$KERNELDIR/include/linux/perf_event.h"
-fi
-AC_CHECK_HEADER($PERF_EVENT_H,PERF_EVENT_H_EXISTS="yes")
-AM_CONDITIONAL(BUILD_FOR_PERF_EVENT, test -n "$PERF_EVENT_H_EXISTS")
-if test "$PERF_EVENT_H_EXISTS" = "yes"; then
-	HAVE_PERF_EVENTS='1'
-	if test "$KERNELDIR" != ""; then
-		PERF_EVENT_FLAGS=" -I$KERNELDIR/include"
-		AC_SUBST(PERF_EVENT_FLAGS)
-	fi
-else
-	HAVE_PERF_EVENTS='0'
-fi
 AC_DEFINE_UNQUOTED(HAVE_PERF_EVENTS, $HAVE_PERF_EVENTS, [Kernel support for perf_events exists])
 AC_CANONICAL_HOST
 if test "$HAVE_PERF_EVENTS" = "1"; then
@@ -414,7 +449,7 @@ elif test "`getent passwd oprofile 2>/de
 	fi
 fi
 
-if  test "$PERF_EVENT_H_EXISTS" != "yes" && test "$kernel_has_perf_events_support" = "yes"; then
+if  test "$PERF_EVENT_H_EXISTS" != "yes" && test "$kernel_may_have_perf_events_support" = "yes"; then
 	echo "Warning: perf_event.h not found.  Please install the kernel headers package if you"
 	echo "         want non-root support built into OProfile."
 fi
diff --git a/utils/op_perf_events_checker.c b/utils/op_perf_events_checker.c
--- a/utils/op_perf_events_checker.c
+++ b/utils/op_perf_events_checker.c
@@ -49,8 +49,10 @@ int main(int argc, char **argv)
 	}
 
 #if HAVE_PERF_EVENTS
-	/* If perf_events syscall is not implemented, the syscall below will fail
-	 * with ENOSYS (38).  If implemented, but the processor type on which this
+	/* Even if the perf_event_open syscall is implemented, the architecture may still
+	 * not provide a full implementation of the perf_events subsystem, in which case,
+	 * the syscall below will fail with ENOSYS (38).  If the perf_events subsystem is
+	 * implemented for the architecture, but the processor type on which this
 	 * program is running is not supported by perf_events, the syscall returns
 	 * ENOENT (2).
 	 */
