diff options
-rw-r--r-- | autoconf/configure.ac | 4 | ||||
-rwxr-xr-x | cmake/config-ix.cmake | 1 | ||||
-rwxr-xr-x | cmake/modules/LLVM-Config.cmake | 7 | ||||
-rwxr-xr-x | configure | 104 | ||||
-rw-r--r-- | include/llvm/Support/Process.h | 20 | ||||
-rw-r--r-- | lib/Support/Process.cpp | 25 | ||||
-rw-r--r-- | lib/Support/Unix/Process.inc | 62 | ||||
-rw-r--r-- | lib/Support/Windows/Process.inc | 51 | ||||
-rw-r--r-- | unittests/Support/ProcessTest.cpp | 7 |
9 files changed, 244 insertions, 37 deletions
diff --git a/autoconf/configure.ac b/autoconf/configure.ac index c5ac14999f..fdda3f9567 100644 --- a/autoconf/configure.ac +++ b/autoconf/configure.ac @@ -1250,6 +1250,10 @@ AC_SEARCH_LIBS(dlopen,dl,AC_DEFINE([HAVE_DLOPEN],[1], [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], diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 274de31c9e..c625b871e4 100755 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -99,6 +99,7 @@ if( NOT PURE_WINDOWS ) endif() endif() check_library_exists(dl dlopen "" HAVE_LIBDL) + check_library_exists(rt clock_gettime "" HAVE_LIBRT) endif() # function checks diff --git a/cmake/modules/LLVM-Config.cmake b/cmake/modules/LLVM-Config.cmake index 574335c49d..163401c857 100755 --- a/cmake/modules/LLVM-Config.cmake +++ b/cmake/modules/LLVM-Config.cmake @@ -4,11 +4,14 @@ function(get_system_libs return_var) 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 ) @@ -12517,6 +12517,110 @@ echo "$as_me: WARNING: dlopen() not found - disabling plugin support" >&2;} 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; } diff --git a/include/llvm/Support/Process.h b/include/llvm/Support/Process.h index f149a4e058..ea8da6d4c5 100644 --- a/include/llvm/Support/Process.h +++ b/include/llvm/Support/Process.h @@ -64,6 +64,23 @@ public: /// \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 @@ class self_process : public process { 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) /// @{ diff --git a/lib/Support/Process.cpp b/lib/Support/Process.cpp index 64a1fa237e..2c0d37bb32 100644 --- a/lib/Support/Process.cpp +++ b/lib/Support/Process.cpp @@ -50,6 +50,31 @@ self_process::~self_process() { 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 diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index 9ad6272bab..a525538f78 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -49,6 +49,43 @@ process::id_type self_process::get_id() { 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 @@ size_t Process::GetMallocUsage() { #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() { diff --git a/lib/Support/Windows/Process.inc b/lib/Support/Windows/Process.inc index 9eb611be87..789ae7ddce 100644 --- a/lib/Support/Windows/Process.inc +++ b/lib/Support/Windows/Process.inc @@ -43,6 +43,36 @@ process::id_type self_process::get_id() { 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 @@ Process::GetMallocUsage() 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() diff --git a/unittests/Support/ProcessTest.cpp b/unittests/Support/ProcessTest.cpp index 58a7c4acaa..e57c0e6eaf 100644 --- a/unittests/Support/ProcessTest.cpp +++ b/unittests/Support/ProcessTest.cpp @@ -30,6 +30,13 @@ TEST(ProcessTest, SelfProcess) { #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 |