Add support for CPUPROFILE_TIMER_SIGNAL environment variable.

Which both enables per-thread timers and allows the signal number for
the timer to be selected.

[alk@tut.by: reformatted commit message for subject line length]
Signed-off-by: Aliaksey Kandratsenka <alk@tut.by>
diff --git a/src/profile-handler.cc b/src/profile-handler.cc
index 1518ed4..66c9d74 100644
--- a/src/profile-handler.cc
+++ b/src/profile-handler.cc
@@ -147,6 +147,9 @@
   // ITIMER_PROF (which uses SIGPROF), or ITIMER_REAL (which uses SIGALRM)
   int timer_type_;
 
+  // Signal number for timer signal.
+  int signal_number_;
+
   // Counts the number of callbacks registered.
   int32 callback_count_ GUARDED_BY(control_lock_);
 
@@ -286,7 +289,8 @@
   }
 }
 
-static void StartLinuxThreadTimer(int timer_type, int32 frequency, pthread_key_t timer_key) {
+static void StartLinuxThreadTimer(int timer_type, int signal_number,
+                                  int32 frequency, pthread_key_t timer_key) {
   int rv;
   struct sigevent sevp;
   timer_t timerid;
@@ -294,7 +298,6 @@
   memset(&sevp, 0, sizeof(sevp));
   sevp.sigev_notify = SIGEV_THREAD_ID;
   sevp._sigev_un._tid = sys_gettid();
-  const int signal_number = (timer_type == ITIMER_PROF ? SIGPROF : SIGALRM);
   sevp.sigev_signo = signal_number;
   clockid_t clock = CLOCK_THREAD_CPUTIME_ID;
   if (timer_type == ITIMER_REAL) {
@@ -348,6 +351,7 @@
   SpinLockHolder cl(&control_lock_);
 
   timer_type_ = (getenv("CPUPROFILE_REALTIME") ? ITIMER_REAL : ITIMER_PROF);
+  signal_number_ = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
 
   // Get frequency of interrupts (if specified)
   char junk;
@@ -364,11 +368,35 @@
     return;
   }
 
+#if HAVE_LINUX_SIGEV_THREAD_ID
+  // Do this early because we might be overriding signal number.
+
+  const char *per_thread = getenv("CPUPROFILE_PER_THREAD_TIMERS");
+  const char *signal_number = getenv("CPUPROFILE_TIMER_SIGNAL");
+
+  if (per_thread || signal_number) {
+    if (timer_create && pthread_once) {
+      timer_sharing_ = TIMERS_SEPARATE;
+      CreateThreadTimerKey(&thread_timer_key);
+      per_thread_timer_enabled_ = true;
+      // Override signal number if requested.
+      if (signal_number) {
+        signal_number_ = strtol(signal_number, NULL, 0);
+      }
+    } else {
+      RAW_LOG(INFO,
+              "Ignoring CPUPROFILE_PER_THREAD_TIMERS and\n"
+              " CPUPROFILE_TIMER_SIGNAL due to lack of timer_create().\n"
+              " Preload or link to librt.so for this to work");
+    }
+  }
+#endif
+
   // If something else is using the signal handler,
   // assume it has priority over us and stop.
   if (!IsSignalHandlerAvailable()) {
-    RAW_LOG(INFO, "Disabling profiler because %s handler is already in use.",
-                    timer_type_ == ITIMER_REAL ? "SIGALRM" : "SIGPROF");
+    RAW_LOG(INFO, "Disabling profiler because signal %d handler is already in use.",
+            signal_number_);
     allowed_ = false;
     return;
   }
@@ -377,19 +405,6 @@
   // should already be ignored.)
   DisableHandler();
 
-#if HAVE_LINUX_SIGEV_THREAD_ID
-  if (getenv("CPUPROFILE_PER_THREAD_TIMERS")) {
-    if (timer_create && pthread_once) {
-      timer_sharing_ = TIMERS_SEPARATE;
-      CreateThreadTimerKey(&thread_timer_key);
-      per_thread_timer_enabled_ = true;
-    } else {
-      RAW_LOG(INFO,
-              "Not enabling linux-per-thread-timers mode due to lack of timer_create."
-              " Preload or link to librt.so for this to work");
-    }
-  }
-#endif
 }
 
 ProfileHandler::~ProfileHandler() {
@@ -538,7 +553,7 @@
 
 #if HAVE_LINUX_SIGEV_THREAD_ID
   if (per_thread_timer_enabled_) {
-    StartLinuxThreadTimer(timer_type_, frequency_, thread_timer_key);
+    StartLinuxThreadTimer(timer_type_, signal_number_, frequency_, thread_timer_key);
     return;
   }
 #endif
@@ -584,8 +599,7 @@
   sa.sa_sigaction = SignalHandler;
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   sigemptyset(&sa.sa_mask);
-  const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
-  RAW_CHECK(sigaction(signal_number, &sa, NULL) == 0, "sigprof (enable)");
+  RAW_CHECK(sigaction(signal_number_, &sa, NULL) == 0, "sigprof (enable)");
 }
 
 void ProfileHandler::DisableHandler() {
@@ -596,14 +610,12 @@
   sa.sa_handler = SIG_IGN;
   sa.sa_flags = SA_RESTART;
   sigemptyset(&sa.sa_mask);
-  const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
-  RAW_CHECK(sigaction(signal_number, &sa, NULL) == 0, "sigprof (disable)");
+  RAW_CHECK(sigaction(signal_number_, &sa, NULL) == 0, "sigprof (disable)");
 }
 
 bool ProfileHandler::IsSignalHandlerAvailable() {
   struct sigaction sa;
-  const int signal_number = (timer_type_ == ITIMER_PROF ? SIGPROF : SIGALRM);
-  RAW_CHECK(sigaction(signal_number, NULL, &sa) == 0, "is-signal-handler avail");
+  RAW_CHECK(sigaction(signal_number_, NULL, &sa) == 0, "is-signal-handler avail");
 
   // We only take over the handler if the current one is unset.
   // It must be SIG_IGN or SIG_DFL, not some other function.
diff --git a/src/tests/profile-handler_unittest.cc b/src/tests/profile-handler_unittest.cc
index e49d23e..72a4640 100644
--- a/src/tests/profile-handler_unittest.cc
+++ b/src/tests/profile-handler_unittest.cc
@@ -212,6 +212,11 @@
     timer_separate_ = threads_have_separate_timers();
 #if HAVE_LINUX_SIGEV_THREAD_ID
     linux_per_thread_timers_mode_ = (getenv("CPUPROFILE_PER_THREAD_TIMERS") != NULL);
+    const char *signal_number = getenv("CPUPROFILE_TIMER_SIGNAL");
+    if (signal_number) {
+      signal_number_ = strtol(signal_number, NULL, 0);
+      linux_per_thread_timers_mode_ = true;
+    }
 #endif
     Delay(kTimerResetInterval);
   }