[llvm-commits] [llvm] r171551 - in /llvm/trunk: autoconf/configure.ac cmake/config-ix.cmake cmake/modules/LLVM-Config.cmake configure include/llvm/Support/Process.h lib/Support/Process.cpp lib/Support/Unix/Process.inc lib/Support/Windows/Process.inc unittests/Support/ProcessTest.cpp

Chandler Carruth chandlerc at gmail.com
Fri Jan 4 15:19:55 PST 2013


Author: chandlerc
Date: Fri Jan  4 17:19:55 2013
New Revision: 171551

URL: http://llvm.org/viewvc/llvm-project?rev=171551&view=rev
Log:
Add time getters to the process interface for requesting the elapsed
wall time, user time, and system time since a process started.

For walltime, we currently use TimeValue's interface and a global
initializer to compute a close approximation of total process runtime.

For user time, this adds support for an somewhat more precise timing
mechanism -- clock_gettime with the CLOCK_PROCESS_CPUTIME_ID clock
selected.

For system time, we have to do a full getrusage call to extract the
system time from the OS. This is expensive but unavoidable.

In passing, clean up the implementation of the old APIs and fix some
latent bugs in the Windows code. This might have manifested on Windows
ARM systems or other systems with strange 64-bit integer behavior.

The old API for this both user time and system time simultaneously from
a single getrusage call. While this results in fewer system calls, it
also results in a lower precision user time and if only user time is
desired, it introduces a higher overhead. It may be worthwhile to switch
some of the pass timers to not track system time and directly track user
and wall time. The old API also tracked walltime in a confusing way --
it just set it to the current walltime rather than providing any measure
of wall time since the process started the way buth user and system time
are tracked. The new API is more consistent here.

The plan is to eventually implement these methods for a *child* process
by using the wait3(2) system call to populate an rusage struct
representing the whole subprocess execution. That way, after waiting on
a child process its stats will become accurate and cheap to query.

Modified:
    llvm/trunk/autoconf/configure.ac
    llvm/trunk/cmake/config-ix.cmake
    llvm/trunk/cmake/modules/LLVM-Config.cmake
    llvm/trunk/configure
    llvm/trunk/include/llvm/Support/Process.h
    llvm/trunk/lib/Support/Process.cpp
    llvm/trunk/lib/Support/Unix/Process.inc
    llvm/trunk/lib/Support/Windows/Process.inc
    llvm/trunk/unittests/Support/ProcessTest.cpp

Modified: llvm/trunk/autoconf/configure.ac
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/autoconf/configure.ac?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/autoconf/configure.ac (original)
+++ llvm/trunk/autoconf/configure.ac Fri Jan  4 17:19:55 2013
@@ -1250,6 +1250,10 @@
                [Define if dlopen() is available on this platform.]),
                AC_MSG_WARN([dlopen() not found - disabling plugin support]))
 
+dnl clock_gettime() is required for modern timing support.
+AC_SEARCH_LIBS(clock_gettime,rt,[],
+               AC_MSG_ERROR([clock_gettime not found and is required for modern POSIX timing uspport]))
+
 dnl libffi is optional; used to call external functions from the interpreter
 if test "$llvm_cv_enable_libffi" = "yes" ; then
   AC_SEARCH_LIBS(ffi_call,ffi,AC_DEFINE([HAVE_FFI_CALL],[1],

Modified: llvm/trunk/cmake/config-ix.cmake
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/cmake/config-ix.cmake?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/cmake/config-ix.cmake (original)
+++ llvm/trunk/cmake/config-ix.cmake Fri Jan  4 17:19:55 2013
@@ -99,6 +99,7 @@
     endif()
   endif()
   check_library_exists(dl dlopen "" HAVE_LIBDL)
+  check_library_exists(rt clock_gettime "" HAVE_LIBRT)
 endif()
 
 # function checks

Modified: llvm/trunk/cmake/modules/LLVM-Config.cmake
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/cmake/modules/LLVM-Config.cmake?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/cmake/modules/LLVM-Config.cmake (original)
+++ llvm/trunk/cmake/modules/LLVM-Config.cmake Fri Jan  4 17:19:55 2013
@@ -4,11 +4,14 @@
     if( MINGW )
       set(system_libs ${system_libs} imagehlp psapi)
     elseif( CMAKE_HOST_UNIX )
+      if( HAVE_LIBRT )
+        set(system_libs ${system_libs} rt)
+      endif()
       if( HAVE_LIBDL )
-	set(system_libs ${system_libs} ${CMAKE_DL_LIBS})
+        set(system_libs ${system_libs} ${CMAKE_DL_LIBS})
       endif()
       if( LLVM_ENABLE_THREADS AND HAVE_LIBPTHREAD )
-	set(system_libs ${system_libs} pthread)
+        set(system_libs ${system_libs} pthread)
       endif()
     endif( MINGW )
   endif( NOT MSVC )

Modified: llvm/trunk/configure
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/configure?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/configure (original)
+++ llvm/trunk/configure Fri Jan  4 17:19:55 2013
@@ -12517,6 +12517,110 @@
 fi
 
 
+{ echo "$as_me:$LINENO: checking for library containing clock_gettime" >&5
+echo $ECHO_N "checking for library containing clock_gettime... $ECHO_C" >&6; }
+if test "${ac_cv_search_clock_gettime+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' rt; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_clock_gettime=$ac_res
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext \
+      conftest$ac_exeext
+  if test "${ac_cv_search_clock_gettime+set}" = set; then
+  break
+fi
+done
+if test "${ac_cv_search_clock_gettime+set}" = set; then
+  :
+else
+  ac_cv_search_clock_gettime=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_clock_gettime" >&5
+echo "${ECHO_T}$ac_cv_search_clock_gettime" >&6; }
+ac_res=$ac_cv_search_clock_gettime
+if test "$ac_res" != no; then
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+  { { echo "$as_me:$LINENO: error: clock_gettime not found and is required for modern POSIX timing uspport" >&5
+echo "$as_me: error: clock_gettime not found and is required for modern POSIX timing uspport" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
 if test "$llvm_cv_enable_libffi" = "yes" ; then
   { echo "$as_me:$LINENO: checking for library containing ffi_call" >&5
 echo $ECHO_N "checking for library containing ffi_call... $ECHO_C" >&6; }

Modified: llvm/trunk/include/llvm/Support/Process.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Process.h?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Process.h (original)
+++ llvm/trunk/include/llvm/Support/Process.h Fri Jan  4 17:19:55 2013
@@ -64,6 +64,23 @@
   /// \brief Get the operating system specific identifier for this process.
   virtual id_type get_id() = 0;
 
+  /// \brief Get the user time consumed by this process.
+  ///
+  /// Note that this is often an approximation and may be zero on platforms
+  /// where we don't have good support for the functionality.
+  virtual TimeValue get_user_time() const = 0;
+
+  /// \brief Get the system time consumed by this process.
+  ///
+  /// Note that this is often an approximation and may be zero on platforms
+  /// where we don't have good support for the functionality.
+  virtual TimeValue get_system_time() const = 0;
+
+  /// \brief Get the wall time consumed by this process.
+  ///
+  /// Note that this is often an approximation and may be zero on platforms
+  /// where we don't have good support for the functionality.
+  virtual TimeValue get_wall_time() const = 0;
 
   /// \name Static factory routines for processes.
   /// @{
@@ -88,6 +105,9 @@
 
 public:
   virtual id_type get_id();
+  virtual TimeValue get_user_time() const;
+  virtual TimeValue get_system_time() const;
+  virtual TimeValue get_wall_time() const;
 
   /// \name Process configuration (sysconf on POSIX)
   /// @{

Modified: llvm/trunk/lib/Support/Process.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Process.cpp?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Process.cpp (original)
+++ llvm/trunk/lib/Support/Process.cpp Fri Jan  4 17:19:55 2013
@@ -50,6 +50,31 @@
   llvm_unreachable("This destructor must never be executed!");
 }
 
+/// \brief A helper function to compute the elapsed wall-time since the program
+/// started.
+///
+/// Note that this routine actually computes the elapsed wall time since the
+/// first time it was called. However, we arrange to have it called during the
+/// startup of the process to get approximately correct results.
+static TimeValue getElapsedWallTime() {
+  static TimeValue &StartTime = *new TimeValue(TimeValue::now());
+  return TimeValue::now() - StartTime;
+}
+
+/// \brief A special global variable to ensure we call \c getElapsedWallTime
+/// during global initialization of the program.
+///
+/// Note that this variable is never referenced elsewhere. Doing so could
+/// create race conditions during program startup or shutdown.
+static volatile TimeValue DummyTimeValue = getElapsedWallTime();
+
+// Implement this routine by using the static helpers above. They're already
+// portable.
+TimeValue self_process::get_wall_time() const {
+  return getElapsedWallTime();
+}
+
+
 #if defined(_MSC_VER)
 #pragma warning(pop)
 #endif

Modified: llvm/trunk/lib/Support/Unix/Process.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Unix/Process.inc?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Unix/Process.inc (original)
+++ llvm/trunk/lib/Support/Unix/Process.inc Fri Jan  4 17:19:55 2013
@@ -49,6 +49,43 @@
   return getpid();
 }
 
+static std::pair<TimeValue, TimeValue> getRUsageTimes() {
+#if defined(HAVE_GETRUSAGE)
+  struct rusage RU;
+  ::getrusage(RUSAGE_SELF, &RU);
+  return std::make_pair(
+      TimeValue(
+          static_cast<TimeValue::SecondsType>(RU.ru_utime.tv_sec),
+          static_cast<TimeValue::NanoSecondsType>(
+              RU.ru_utime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)),
+      TimeValue(
+          static_cast<TimeValue::SecondsType>(RU.ru_stime.tv_sec),
+          static_cast<TimeValue::NanoSecondsType>(
+              RU.ru_stime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)));
+#else
+#warning Cannot get usage times on this platform
+  return std::make_pair(TimeValue(), TimeValue());
+#endif
+}
+
+TimeValue self_process::get_user_time() const {
+#ifdef _POSIX_CPUTIME
+  // Try to get a high resolution CPU timer.
+  struct timespec TS;
+  if (::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &TS) == 0)
+    return TimeValue(static_cast<TimeValue::SecondsType>(TS.tv_sec),
+                     static_cast<TimeValue::NanoSecondsType>(TS.tv_nsec));
+#endif
+
+  // Otherwise fall back to rusage based timing.
+  return getRUsageTimes().first;
+}
+
+TimeValue self_process::get_system_time() const {
+  // We can only collect system time by inspecting the results of getrusage.
+  return getRUsageTimes().second;
+}
+
 static unsigned getPageSize() {
 #if defined(__CYGWIN__)
   // On Cygwin, getpagesize() returns 64k but the page size for the purposes of
@@ -95,29 +132,10 @@
 #endif
 }
 
-void
-Process::GetTimeUsage(TimeValue& elapsed, TimeValue& user_time,
-                      TimeValue& sys_time)
-{
+void Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time,
+                           TimeValue &sys_time) {
   elapsed = TimeValue::now();
-#if defined(HAVE_GETRUSAGE)
-  struct rusage usage;
-  ::getrusage(RUSAGE_SELF, &usage);
-  user_time = TimeValue(
-    static_cast<TimeValue::SecondsType>( usage.ru_utime.tv_sec ),
-    static_cast<TimeValue::NanoSecondsType>( usage.ru_utime.tv_usec *
-      TimeValue::NANOSECONDS_PER_MICROSECOND ) );
-  sys_time = TimeValue(
-    static_cast<TimeValue::SecondsType>( usage.ru_stime.tv_sec ),
-    static_cast<TimeValue::NanoSecondsType>( usage.ru_stime.tv_usec *
-      TimeValue::NANOSECONDS_PER_MICROSECOND ) );
-#else
-#warning Cannot get usage times on this platform
-  user_time.seconds(0);
-  user_time.microseconds(0);
-  sys_time.seconds(0);
-  sys_time.microseconds(0);
-#endif
+  llvm::tie(user_time, sys_time) = getRUsageTimes();
 }
 
 int Process::GetCurrentUserId() {

Modified: llvm/trunk/lib/Support/Windows/Process.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/Process.inc?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/Process.inc (original)
+++ llvm/trunk/lib/Support/Windows/Process.inc Fri Jan  4 17:19:55 2013
@@ -43,6 +43,36 @@
   return GetCurrentProcess();
 }
 
+static TimeValue getTimeValueFromFILETIME(FILETIME Time) {
+  ULARGE_INTEGER TimeInteger;
+  TimeInteger.LowPart = Time.dwLowDateTime;
+  TimeInteger.HighPart = Time.dwHighDateTime;
+
+  // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
+  return TimeValue(
+      static_cast<TimeValue::SecondsType>(TimeInteger.QuadPart / 10000000),
+      static_cast<TimeValue::NanoSecondsType>(
+          (TimeInteger.QuadPart % 10000000) * 100));
+}
+
+TimeValue self_process::get_user_time() const {
+  FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
+  if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
+                      &UserTime) == 0)
+    return TimeValue();
+
+  return getTimeValueFromFILETIME(UserTime);
+}
+
+TimeValue self_process::get_system_time() const {
+  FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
+  if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
+                      &UserTime) == 0)
+    return TimeValue();
+
+  return getTimeValueFromFILETIME(KernelTime);
+}
+
 // This function retrieves the page size using GetSystemInfo and is present
 // solely so it can be called once to initialize the self_process member below.
 static unsigned getPageSize() {
@@ -76,22 +106,17 @@
   return size;
 }
 
-void
-Process::GetTimeUsage(
-  TimeValue& elapsed, TimeValue& user_time, TimeValue& sys_time)
-{
+void Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time,
+                           TimeValue &sys_time) {
   elapsed = TimeValue::now();
 
-  uint64_t ProcCreate, ProcExit, KernelTime, UserTime;
-  GetProcessTimes(GetCurrentProcess(), (FILETIME*)&ProcCreate,
-                  (FILETIME*)&ProcExit, (FILETIME*)&KernelTime,
-                  (FILETIME*)&UserTime);
+  FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
+  if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
+                      &UserTime) == 0)
+    return;
 
-  // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
-  user_time.seconds( UserTime / 10000000 );
-  user_time.nanoseconds( unsigned(UserTime % 10000000) * 100 );
-  sys_time.seconds( KernelTime / 10000000 );
-  sys_time.nanoseconds( unsigned(KernelTime % 10000000) * 100 );
+  user_time = getTimeValueFromFILETIME(UserTime);
+  sys_time = getTimeValueFromFILETIME(SystemTime);
 }
 
 int Process::GetCurrentUserId()

Modified: llvm/trunk/unittests/Support/ProcessTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/ProcessTest.cpp?rev=171551&r1=171550&r2=171551&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/ProcessTest.cpp (original)
+++ llvm/trunk/unittests/Support/ProcessTest.cpp Fri Jan  4 17:19:55 2013
@@ -30,6 +30,13 @@
 #endif
 
   EXPECT_LT(1u, process::get_self()->page_size());
+
+  EXPECT_LT(TimeValue::MinTime, process::get_self()->get_user_time());
+  EXPECT_GT(TimeValue::MaxTime, process::get_self()->get_user_time());
+  EXPECT_LT(TimeValue::MinTime, process::get_self()->get_system_time());
+  EXPECT_GT(TimeValue::MaxTime, process::get_self()->get_system_time());
+  EXPECT_LT(TimeValue::MinTime, process::get_self()->get_wall_time());
+  EXPECT_GT(TimeValue::MaxTime, process::get_self()->get_wall_time());
 }
 
 } // end anonymous namespace





More information about the llvm-commits mailing list