diff --git a/lib/process.c b/lib/process.c
index e9d0ba9..3e119b5 100644
--- a/lib/process.c
+++ b/lib/process.c
@@ -33,6 +33,7 @@
 #include "poll-loop.h"
 #include "signals.h"
 #include "socket-util.h"
+#include "timeval.h"
 #include "util.h"
 #include "openvswitch/vlog.h"
 
@@ -40,6 +41,13 @@ VLOG_DEFINE_THIS_MODULE(process);
 
 COVERAGE_DEFINE(process_start);
 
+#ifdef __linux__
+#define LINUX 1
+#include <asm/param.h>
+#else
+#define LINUX 0
+#endif
+
 struct process {
     struct ovs_list node;
     char *name;
@@ -50,6 +58,15 @@ struct process {
     int status;
 };
 
+struct raw_process_info {
+    unsigned long int vsz;      /* Virtual size, in kB. */
+    unsigned long int rss;      /* Resident set size, in kB. */
+    long long int uptime;       /* ms since started. */
+    long long int cputime;      /* ms of CPU used during 'uptime'. */
+    pid_t ppid;                 /* Parent. */
+    char name[18];              /* Name (surrounded by parentheses). */
+};
+
 /* Pipe used to signal child termination. */
 static int fds[2];
 
@@ -327,6 +344,178 @@ process_status(const struct process *p)
     return p->status;
 }
 
+int
+count_crashes(pid_t pid)
+{
+    char file_name[128];
+    const char *paren;
+    char line[128];
+    int crashes = 0;
+    FILE *stream;
+
+    ovs_assert(LINUX);
+
+    sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid);
+    stream = fopen(file_name, "r");
+    if (!stream) {
+        VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
+        goto exit;
+    }
+
+    if (!fgets(line, sizeof line, stream)) {
+        VLOG_WARN_ONCE("%s: read failed (%s)", file_name,
+                       feof(stream) ? "end of file" : ovs_strerror(errno));
+        goto exit_close;
+    }
+
+    paren = strchr(line, '(');
+    if (paren) {
+        int x;
+        if (ovs_scan(paren + 1, "%d", &x)) {
+            crashes = x;
+        }
+    }
+
+exit_close:
+    fclose(stream);
+exit:
+    return crashes;
+}
+
+static unsigned long long int
+ticks_to_ms(unsigned long long int ticks)
+{
+    ovs_assert(LINUX);
+
+#ifndef USER_HZ
+#define USER_HZ 100
+#endif
+
+#if USER_HZ == 100              /* Common case. */
+    return ticks * (1000 / USER_HZ);
+#else  /* Alpha and some other architectures.  */
+    double factor = 1000.0 / USER_HZ;
+    return ticks * factor + 0.5;
+#endif
+}
+
+static bool
+get_raw_process_info(pid_t pid, struct raw_process_info *raw)
+{
+    unsigned long long int vsize, rss, start_time, utime, stime;
+    long long int start_msec;
+    unsigned long ppid;
+    char file_name[128];
+    FILE *stream;
+    int n;
+
+    ovs_assert(LINUX);
+
+    sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid);
+    stream = fopen(file_name, "r");
+    if (!stream) {
+        VLOG_ERR_ONCE("%s: open failed (%s)",
+                      file_name, ovs_strerror(errno));
+        return false;
+    }
+
+    n = fscanf(stream,
+               "%*d "           /* (1. pid) */
+               "%17s "          /* 2. process name */
+               "%*c "           /* (3. state) */
+               "%lu "           /* 4. ppid */
+               "%*d "           /* (5. pgid) */
+               "%*d "           /* (6. sid) */
+               "%*d "           /* (7. tty_nr) */
+               "%*d "           /* (8. tty_pgrp) */
+               "%*u "           /* (9. flags) */
+               "%*u "           /* (10. min_flt) */
+               "%*u "           /* (11. cmin_flt) */
+               "%*u "           /* (12. maj_flt) */
+               "%*u "           /* (13. cmaj_flt) */
+               "%llu "          /* 14. utime */
+               "%llu "          /* 15. stime */
+               "%*d "           /* (16. cutime) */
+               "%*d "           /* (17. cstime) */
+               "%*d "           /* (18. priority) */
+               "%*d "           /* (19. nice) */
+               "%*d "           /* (20. num_threads) */
+               "%*d "           /* (21. always 0) */
+               "%llu "          /* 22. start_time */
+               "%llu "          /* 23. vsize */
+               "%llu "          /* 24. rss */
+#if 0
+               /* These are here for documentation but #if'd out to save
+                * actually parsing them from the stream for no benefit. */
+               "%*lu "          /* (25. rsslim) */
+               "%*lu "          /* (26. start_code) */
+               "%*lu "          /* (27. end_code) */
+               "%*lu "          /* (28. start_stack) */
+               "%*lu "          /* (29. esp) */
+               "%*lu "          /* (30. eip) */
+               "%*lu "          /* (31. pending signals) */
+               "%*lu "          /* (32. blocked signals) */
+               "%*lu "          /* (33. ignored signals) */
+               "%*lu "          /* (34. caught signals) */
+               "%*lu "          /* (35. whcan) */
+               "%*lu "          /* (36. always 0) */
+               "%*lu "          /* (37. always 0) */
+               "%*d "           /* (38. exit_signal) */
+               "%*d "           /* (39. task_cpu) */
+               "%*u "           /* (40. rt_priority) */
+               "%*u "           /* (41. policy) */
+               "%*llu "         /* (42. blkio_ticks) */
+               "%*lu "          /* (43. gtime) */
+               "%*ld"           /* (44. cgtime) */
+#endif
+               , raw->name, &ppid, &utime, &stime, &start_time, &vsize, &rss);
+    fclose(stream);
+    if (n != 7) {
+        VLOG_ERR_ONCE("%s: fscanf failed", file_name);
+        return false;
+    }
+
+    start_msec = get_boot_time() + ticks_to_ms(start_time);
+
+    raw->vsz = vsize / 1024;
+    raw->rss = rss * (get_page_size() / 1024);
+    raw->uptime = time_wall_msec() - start_msec;
+    raw->cputime = ticks_to_ms(utime + stime);
+    raw->ppid = ppid;
+
+    return true;
+}
+
+bool
+get_process_info(pid_t pid, struct process_info *pinfo)
+{
+    struct raw_process_info child;
+
+    ovs_assert(LINUX);
+    if (!get_raw_process_info(pid, &child)) {
+        return false;
+    }
+
+    pinfo->vsz = child.vsz;
+    pinfo->rss = child.rss;
+    pinfo->booted = child.uptime;
+    pinfo->crashes = 0;
+    pinfo->uptime = child.uptime;
+    pinfo->cputime = child.cputime;
+
+    if (child.ppid) {
+        struct raw_process_info parent;
+
+        get_raw_process_info(child.ppid, &parent);
+        if (!strcmp(child.name, parent.name)) {
+            pinfo->booted = parent.uptime;
+            pinfo->crashes = count_crashes(child.ppid);
+        }
+    }
+
+    return true;
+}
+
 /* Given 'status', which is a process status in the form reported by waitpid(2)
  * and returned by process_status(), returns a string describing how the
  * process terminated.  The caller is responsible for freeing the string when
diff --git a/lib/process.h b/lib/process.h
index 3feac7e..999ac68 100644
--- a/lib/process.h
+++ b/lib/process.h
@@ -22,6 +22,15 @@
 
 struct process;
 
+struct process_info {
+    unsigned long int vsz;      /* Virtual size, in kB. */
+    unsigned long int rss;      /* Resident set size, in kB. */
+    long long int booted;       /* ms since monitor started. */
+    int crashes;                /* # of crashes (usually 0). */
+    long long int uptime;       /* ms since last (re)started by monitor. */
+    long long int cputime;      /* ms of CPU used during 'uptime'. */
+};
+
 /* Starting and monitoring subprocesses.
  *
  * process_init() and process_start() may safely be called only from a
@@ -39,6 +48,9 @@ int process_status(const struct process *);
 void process_run(void);
 void process_wait(struct process *);
 
+int count_crashes(pid_t);
+bool get_process_info(pid_t, struct process_info *);
+
 /* These functions are thread-safe. */
 char *process_status_msg(int);
 char *process_escape_args(char **argv);
diff --git a/lib/util.c b/lib/util.c
index 85b9350..b832ac1 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -33,6 +33,7 @@
 #include "ovs-rcu.h"
 #include "ovs-thread.h"
 #include "socket-util.h"
+#include "timeval.h"
 #include "openvswitch/vlog.h"
 #ifdef HAVE_PTHREAD_SET_NAME_NP
 #include <pthread_np.h>
@@ -40,6 +41,13 @@
 
 VLOG_DEFINE_THIS_MODULE(util);
 
+#ifdef __linux__
+#define LINUX 1
+#include <asm/param.h>
+#else
+#define LINUX 0
+#endif
+
 COVERAGE_DEFINE(util_xalloc);
 
 /* argv[0] without directory names. */
@@ -538,6 +546,66 @@ set_subprogram_name(const char *subprogram_name)
 #endif
 }
 
+unsigned int
+get_page_size(void)
+{
+    static unsigned int cached;
+
+    if (!cached) {
+#ifndef _WIN32
+        long int value = sysconf(_SC_PAGESIZE);
+#else
+        long int value;
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        value = sysinfo.dwPageSize;
+#endif
+        if (value >= 0) {
+            cached = value;
+        }
+    }
+
+    return cached;
+}
+
+/* Returns the time at which the system booted, as the number of milliseconds
+ * since the epoch, or 0 if the time of boot cannot be determined. */
+long long int
+get_boot_time(void)
+{
+    static long long int cache_expiration = LLONG_MIN;
+    static long long int boot_time;
+
+    ovs_assert(LINUX);
+
+    if (time_msec() >= cache_expiration) {
+        static const char stat_file[] = "/proc/stat";
+        char line[128];
+        FILE *stream;
+
+        cache_expiration = time_msec() + 5 * 1000;
+
+        stream = fopen(stat_file, "r");
+        if (!stream) {
+            VLOG_ERR_ONCE("%s: open failed (%s)",
+                          stat_file, ovs_strerror(errno));
+            return boot_time;
+        }
+
+        while (fgets(line, sizeof line, stream)) {
+            long long int btime;
+            if (ovs_scan(line, "btime %lld", &btime)) {
+                boot_time = btime * 1000;
+                goto done;
+            }
+        }
+        VLOG_ERR_ONCE("%s: btime not found", stat_file);
+    done:
+        fclose(stream);
+    }
+    return boot_time;
+}
+
 /* Returns a pointer to a string describing the program version.  The
  * caller must not modify or free the returned string.
  */
diff --git a/lib/util.h b/lib/util.h
index c2d1c3f..4706c99 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -112,6 +112,9 @@ extern "C" {
 const char *get_subprogram_name(void);
     void set_subprogram_name(const char *);
 
+unsigned int get_page_size(void);
+long long int get_boot_time(void);
+
 void ovs_print_version(uint8_t min_ofp, uint8_t max_ofp);
 
 OVS_NO_RETURN void out_of_memory(void);
diff --git a/vswitchd/system-stats.c b/vswitchd/system-stats.c
index 553b8fc..c53a9a8 100644
--- a/vswitchd/system-stats.c
+++ b/vswitchd/system-stats.c
@@ -41,6 +41,7 @@
 #include "ovs-thread.h"
 #include "poll-loop.h"
 #include "openvswitch/shash.h"
+#include "process.h"
 #include "smap.h"
 #include "timeval.h"
 #include "openvswitch/vlog.h"
@@ -80,28 +81,6 @@ get_load_average(struct smap *stats OVS_UNUSED)
 #endif
 }
 
-static unsigned int
-get_page_size(void)
-{
-    static unsigned int cached;
-
-    if (!cached) {
-#ifndef _WIN32
-        long int value = sysconf(_SC_PAGESIZE);
-#else
-        long int value;
-        SYSTEM_INFO sysinfo;
-        GetSystemInfo(&sysinfo);
-        value = sysinfo.dwPageSize;
-#endif
-        if (value >= 0) {
-            cached = value;
-        }
-    }
-
-    return cached;
-}
-
 static void
 get_memory_stats(struct smap *stats)
 {
@@ -183,234 +162,6 @@ get_memory_stats(struct smap *stats)
     }
 }
 
-/* Returns the time at which the system booted, as the number of milliseconds
- * since the epoch, or 0 if the time of boot cannot be determined. */
-static long long int
-get_boot_time(void)
-{
-    static long long int cache_expiration = LLONG_MIN;
-    static long long int boot_time;
-
-    ovs_assert(LINUX);
-
-    if (time_msec() >= cache_expiration) {
-        static const char stat_file[] = "/proc/stat";
-        char line[128];
-        FILE *stream;
-
-        cache_expiration = time_msec() + 5 * 1000;
-
-        stream = fopen(stat_file, "r");
-        if (!stream) {
-            VLOG_ERR_ONCE("%s: open failed (%s)",
-                          stat_file, ovs_strerror(errno));
-            return boot_time;
-        }
-
-        while (fgets(line, sizeof line, stream)) {
-            long long int btime;
-            if (ovs_scan(line, "btime %lld", &btime)) {
-                boot_time = btime * 1000;
-                goto done;
-            }
-        }
-        VLOG_ERR_ONCE("%s: btime not found", stat_file);
-    done:
-        fclose(stream);
-    }
-    return boot_time;
-}
-
-static unsigned long long int
-ticks_to_ms(unsigned long long int ticks)
-{
-    ovs_assert(LINUX);
-
-#ifndef USER_HZ
-#define USER_HZ 100
-#endif
-
-#if USER_HZ == 100              /* Common case. */
-    return ticks * (1000 / USER_HZ);
-#else  /* Alpha and some other architectures.  */
-    double factor = 1000.0 / USER_HZ;
-    return ticks * factor + 0.5;
-#endif
-}
-
-struct raw_process_info {
-    unsigned long int vsz;      /* Virtual size, in kB. */
-    unsigned long int rss;      /* Resident set size, in kB. */
-    long long int uptime;       /* ms since started. */
-    long long int cputime;      /* ms of CPU used during 'uptime'. */
-    pid_t ppid;                 /* Parent. */
-    char name[18];              /* Name (surrounded by parentheses). */
-};
-
-static bool
-get_raw_process_info(pid_t pid, struct raw_process_info *raw)
-{
-    unsigned long long int vsize, rss, start_time, utime, stime;
-    long long int start_msec;
-    unsigned long ppid;
-    char file_name[128];
-    FILE *stream;
-    int n;
-
-    ovs_assert(LINUX);
-
-    sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid);
-    stream = fopen(file_name, "r");
-    if (!stream) {
-        VLOG_ERR_ONCE("%s: open failed (%s)",
-                      file_name, ovs_strerror(errno));
-        return false;
-    }
-
-    n = fscanf(stream,
-               "%*d "           /* (1. pid) */
-               "%17s "          /* 2. process name */
-               "%*c "           /* (3. state) */
-               "%lu "           /* 4. ppid */
-               "%*d "           /* (5. pgid) */
-               "%*d "           /* (6. sid) */
-               "%*d "           /* (7. tty_nr) */
-               "%*d "           /* (8. tty_pgrp) */
-               "%*u "           /* (9. flags) */
-               "%*u "           /* (10. min_flt) */
-               "%*u "           /* (11. cmin_flt) */
-               "%*u "           /* (12. maj_flt) */
-               "%*u "           /* (13. cmaj_flt) */
-               "%llu "          /* 14. utime */
-               "%llu "          /* 15. stime */
-               "%*d "           /* (16. cutime) */
-               "%*d "           /* (17. cstime) */
-               "%*d "           /* (18. priority) */
-               "%*d "           /* (19. nice) */
-               "%*d "           /* (20. num_threads) */
-               "%*d "           /* (21. always 0) */
-               "%llu "          /* 22. start_time */
-               "%llu "          /* 23. vsize */
-               "%llu "          /* 24. rss */
-#if 0
-               /* These are here for documentation but #if'd out to save
-                * actually parsing them from the stream for no benefit. */
-               "%*lu "          /* (25. rsslim) */
-               "%*lu "          /* (26. start_code) */
-               "%*lu "          /* (27. end_code) */
-               "%*lu "          /* (28. start_stack) */
-               "%*lu "          /* (29. esp) */
-               "%*lu "          /* (30. eip) */
-               "%*lu "          /* (31. pending signals) */
-               "%*lu "          /* (32. blocked signals) */
-               "%*lu "          /* (33. ignored signals) */
-               "%*lu "          /* (34. caught signals) */
-               "%*lu "          /* (35. whcan) */
-               "%*lu "          /* (36. always 0) */
-               "%*lu "          /* (37. always 0) */
-               "%*d "           /* (38. exit_signal) */
-               "%*d "           /* (39. task_cpu) */
-               "%*u "           /* (40. rt_priority) */
-               "%*u "           /* (41. policy) */
-               "%*llu "         /* (42. blkio_ticks) */
-               "%*lu "          /* (43. gtime) */
-               "%*ld"           /* (44. cgtime) */
-#endif
-               , raw->name, &ppid, &utime, &stime, &start_time, &vsize, &rss);
-    fclose(stream);
-    if (n != 7) {
-        VLOG_ERR_ONCE("%s: fscanf failed", file_name);
-        return false;
-    }
-
-    start_msec = get_boot_time() + ticks_to_ms(start_time);
-
-    raw->vsz = vsize / 1024;
-    raw->rss = rss * (get_page_size() / 1024);
-    raw->uptime = time_wall_msec() - start_msec;
-    raw->cputime = ticks_to_ms(utime + stime);
-    raw->ppid = ppid;
-
-    return true;
-}
-
-static int
-count_crashes(pid_t pid)
-{
-    char file_name[128];
-    const char *paren;
-    char line[128];
-    int crashes = 0;
-    FILE *stream;
-
-    ovs_assert(LINUX);
-
-    sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid);
-    stream = fopen(file_name, "r");
-    if (!stream) {
-        VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno));
-        goto exit;
-    }
-
-    if (!fgets(line, sizeof line, stream)) {
-        VLOG_WARN_ONCE("%s: read failed (%s)", file_name,
-                       feof(stream) ? "end of file" : ovs_strerror(errno));
-        goto exit_close;
-    }
-
-    paren = strchr(line, '(');
-    if (paren) {
-        int x;
-        if (ovs_scan(paren + 1, "%d", &x)) {
-            crashes = x;
-        }
-    }
-
-exit_close:
-    fclose(stream);
-exit:
-    return crashes;
-}
-
-struct process_info {
-    unsigned long int vsz;      /* Virtual size, in kB. */
-    unsigned long int rss;      /* Resident set size, in kB. */
-    long long int booted;       /* ms since monitor started. */
-    int crashes;                /* # of crashes (usually 0). */
-    long long int uptime;       /* ms since last (re)started by monitor. */
-    long long int cputime;      /* ms of CPU used during 'uptime'. */
-};
-
-static bool
-get_process_info(pid_t pid, struct process_info *pinfo)
-{
-    struct raw_process_info child;
-
-    ovs_assert(LINUX);
-    if (!get_raw_process_info(pid, &child)) {
-        return false;
-    }
-
-    pinfo->vsz = child.vsz;
-    pinfo->rss = child.rss;
-    pinfo->booted = child.uptime;
-    pinfo->crashes = 0;
-    pinfo->uptime = child.uptime;
-    pinfo->cputime = child.cputime;
-
-    if (child.ppid) {
-        struct raw_process_info parent;
-
-        get_raw_process_info(child.ppid, &parent);
-        if (!strcmp(child.name, parent.name)) {
-            pinfo->booted = parent.uptime;
-            pinfo->crashes = count_crashes(child.ppid);
-        }
-    }
-
-    return true;
-}
-
 static void
 get_process_stats(struct smap *stats)
 {
