X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Ftimeval.c;h=c8c02bdc425746648998d74df27a3677e466eae3;hb=6d4605fd85630eb3a9ec743763114f286373a544;hp=099730e8f921a30e5bc48bbeacb748febd0750f3;hpb=279c9e030818e039bb1c26be76e2cc1e8f6b13a6;p=openvswitch diff --git a/lib/timeval.c b/lib/timeval.c index 099730e8..c8c02bdc 100644 --- a/lib/timeval.c +++ b/lib/timeval.c @@ -20,13 +20,16 @@ #include #include #include +#include #include #include #include #include #include "coverage.h" +#include "dummy.h" #include "fatal-signal.h" #include "signals.h" +#include "unixctl.h" #include "util.h" #include "vlog.h" @@ -48,6 +51,9 @@ static volatile sig_atomic_t monotonic_tick = true; static struct timespec wall_time; static struct timespec monotonic_time; +/* Fixed monotonic time offset, for use by unit tests. */ +static struct timespec warp_offset; + /* Time at which to die with SIGALRM (if not TIME_MIN). */ static time_t deadline = TIME_MIN; @@ -59,8 +65,11 @@ static void refresh_monotonic_if_ticked(void); static time_t time_add(time_t, time_t); static void block_sigalrm(sigset_t *); static void unblock_sigalrm(const sigset_t *); -static void log_poll_interval(long long int last_wakeup, - const struct rusage *last_rusage); +static void log_poll_interval(long long int last_wakeup); +static struct rusage *get_recent_rusage(void); +static void refresh_rusage(void); +static void timespec_add(struct timespec *sum, + const struct timespec *a, const struct timespec *b); /* Initializes the timetracking module. * @@ -179,6 +188,7 @@ refresh_monotonic(void) refresh_wall_if_ticked(); monotonic_time = wall_time; } + timespec_add(&monotonic_time, &monotonic_time, &warp_offset); monotonic_tick = false; } @@ -267,37 +277,44 @@ time_alarm(unsigned int secs) } /* Like poll(), except: + * + * - The timeout is specified as an absolute time, as defined by + * time_msec(), instead of a duration. * * - On error, returns a negative error code (instead of setting errno). * * - If interrupted by a signal, retries automatically until the original - * 'timeout' expires. (Because of this property, this function will + * timeout is reached. (Because of this property, this function will * never return -EINTR.) * * - As a side effect, refreshes the current time (like time_refresh()). - */ + * + * Stores the number of milliseconds elapsed during poll in '*elapsed'. */ int -time_poll(struct pollfd *pollfds, int n_pollfds, int timeout) +time_poll(struct pollfd *pollfds, int n_pollfds, long long int timeout_when, + int *elapsed) { static long long int last_wakeup; - static struct rusage last_rusage; long long int start; sigset_t oldsigs; bool blocked; int retval; time_refresh(); - log_poll_interval(last_wakeup, &last_rusage); + log_poll_interval(last_wakeup); coverage_clear(); start = time_msec(); blocked = false; for (;;) { + long long int now = time_msec(); int time_left; - if (timeout > 0) { - long long int elapsed = time_msec() - start; - time_left = timeout >= elapsed ? timeout - elapsed : 0; + + if (now >= timeout_when) { + time_left = 0; + } else if ((unsigned long long int) timeout_when - now > INT_MAX) { + time_left = INT_MAX; } else { - time_left = timeout; + time_left = timeout_when - now; } retval = poll(pollfds, n_pollfds, time_left); @@ -318,7 +335,8 @@ time_poll(struct pollfd *pollfds, int n_pollfds, int timeout) unblock_sigalrm(&oldsigs); } last_wakeup = time_msec(); - getrusage(RUSAGE_SELF, &last_rusage); + refresh_rusage(); + *elapsed = last_wakeup - start; return retval; } @@ -399,7 +417,24 @@ timeval_diff_msec(const struct timeval *a, const struct timeval *b) } static void -log_poll_interval(long long int last_wakeup, const struct rusage *last_rusage) +timespec_add(struct timespec *sum, + const struct timespec *a, + const struct timespec *b) +{ + struct timespec tmp; + + tmp.tv_sec = a->tv_sec + b->tv_sec; + tmp.tv_nsec = a->tv_nsec + b->tv_nsec; + if (tmp.tv_nsec >= 1000 * 1000 * 1000) { + tmp.tv_nsec -= 1000 * 1000 * 1000; + tmp.tv_sec++; + } + + *sum = tmp; +} + +static void +log_poll_interval(long long int last_wakeup) { static unsigned int mean_interval; /* In 16ths of a millisecond. */ static unsigned int n_samples; @@ -415,6 +450,7 @@ log_poll_interval(long long int last_wakeup, const struct rusage *last_rusage) /* Warn if we took too much time between polls: at least 50 ms and at least * 8X the mean interval. */ if (n_samples > 10 && interval > mean_interval * 8 && interval > 50 * 16) { + const struct rusage *last_rusage = get_recent_rusage(); struct rusage rusage; getrusage(RUSAGE_SELF, &rusage); @@ -460,3 +496,83 @@ log_poll_interval(long long int last_wakeup, const struct rusage *last_rusage) mean_interval = interval; } } + +/* CPU usage tracking. */ + +struct cpu_usage { + long long int when; /* Time that this sample was taken. */ + unsigned long long int cpu; /* Total user+system CPU usage when sampled. */ +}; + +static struct rusage recent_rusage; +static struct cpu_usage older = { LLONG_MIN, 0 }; +static struct cpu_usage newer = { LLONG_MIN, 0 }; +static int cpu_usage = -1; + +static struct rusage * +get_recent_rusage(void) +{ + return &recent_rusage; +} + +static void +refresh_rusage(void) +{ + long long int now; + + now = time_msec(); + getrusage(RUSAGE_SELF, &recent_rusage); + + if (now >= newer.when + 3 * 1000) { + older = newer; + newer.when = now; + newer.cpu = (timeval_to_msec(&recent_rusage.ru_utime) + + timeval_to_msec(&recent_rusage.ru_stime)); + + if (older.when != LLONG_MIN && newer.cpu > older.cpu) { + unsigned int dividend = newer.cpu - older.cpu; + unsigned int divisor = (newer.when - older.when) / 100; + cpu_usage = divisor > 0 ? dividend / divisor : -1; + } else { + cpu_usage = -1; + } + } +} + +/* Returns an estimate of this process's CPU usage, as a percentage, over the + * past few seconds of wall-clock time. Returns -1 if no estimate is available + * (which will happen if the process has not been running long enough to have + * an estimate, and can happen for other reasons as well). */ +int +get_cpu_usage(void) +{ + return cpu_usage; +} + +/* Unixctl interface. */ + +static void +timeval_warp_cb(struct unixctl_conn *conn, + int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) +{ + struct timespec ts; + int msecs; + + msecs = atoi(argv[1]); + if (msecs <= 0) { + unixctl_command_reply(conn, 501, "invalid MSECS"); + return; + } + + ts.tv_sec = msecs / 1000; + ts.tv_nsec = (msecs % 1000) * 1000 * 1000; + timespec_add(&warp_offset, &warp_offset, &ts); + unixctl_command_reply(conn, 200, "warped"); +} + +void +timeval_dummy_register(void) +{ + unixctl_command_register("time/warp", "MSECS", 1, 1, + timeval_warp_cb, NULL); +}