[libvirt PATCH 01/13] util: Helper functions to get process info

Vineeth Pillai posted 13 patches 4 years, 3 months ago
There is a newer version of this series
[libvirt PATCH 01/13] util: Helper functions to get process info
Posted by Vineeth Pillai 4 years, 3 months ago
These helper methods are used to capture vcpu information
in ch driver.

Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
---
 src/util/virprocess.c | 136 ++++++++++++++++++++++++++++++++++++++++++
 src/util/virprocess.h |   5 ++
 2 files changed, 141 insertions(+)

diff --git a/src/util/virprocess.c b/src/util/virprocess.c
index 6de3f36f52..0164d70df6 100644
--- a/src/util/virprocess.c
+++ b/src/util/virprocess.c
@@ -1721,3 +1721,139 @@ virProcessSetScheduler(pid_t pid G_GNUC_UNUSED,
 }
 
 #endif /* !WITH_SCHED_SETSCHEDULER */
+
+/*
+TODO: This method was cloned from qemuGetProcessInfo in src/qemu/qemu_driver.c.
+Need to refactor qemu driver to use this shared function.
+*/
+int
+virProcessGetStatInfo(unsigned long long *cpuTime, int *lastCpu, long *vm_rss,
+                   pid_t pid, pid_t tid)
+{
+    g_autofree char *proc = NULL;
+    FILE *pidinfo;
+    unsigned long long usertime = 0, systime = 0;
+    long rss = 0;
+    int cpu = 0;
+
+    /* In general, we cannot assume pid_t fits in int; but /proc parsing
+     * is specific to Linux where int works fine.  */
+    if (tid)
+        proc = g_strdup_printf("/proc/%d/task/%d/stat", (int)pid, tid);
+    else
+        proc = g_strdup_printf("/proc/%d/stat", (int)pid);
+    if (!proc)
+        return -1;
+
+    pidinfo = fopen(proc, "r");
+
+    /* See 'man proc' for information about what all these fields are. We're
+     * only interested in a very few of them */
+    if (!pidinfo ||
+        fscanf(pidinfo,
+               /* pid -> stime */
+               "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu"
+               /* cutime -> endcode */
+               "%*d %*d %*d %*d %*d %*d %*u %*u %ld %*u %*u %*u"
+               /* startstack -> processor */
+               "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %d",
+               &usertime, &systime, &rss, &cpu) != 4) {
+        VIR_WARN("cannot parse process status data");
+    }
+
+    /* We got jiffies
+     * We want nanoseconds
+     * _SC_CLK_TCK is jiffies per second
+     * So calculate thus....
+     */
+    if (cpuTime)
+        *cpuTime = 1000ull * 1000ull * 1000ull * (usertime + systime)
+            / (unsigned long long)sysconf(_SC_CLK_TCK);
+    if (lastCpu)
+        *lastCpu = cpu;
+
+    if (vm_rss)
+        *vm_rss = rss * virGetSystemPageSizeKB();
+
+
+    VIR_DEBUG("Got status for %d/%d user=%llu sys=%llu cpu=%d rss=%ld",
+              (int)pid, tid, usertime, systime, cpu, rss);
+
+    VIR_FORCE_FCLOSE(pidinfo);
+
+    return 0;
+}
+/*
+TODO: This method was cloned from qemuGetSchedInfo in src/qemu/qemu_driver.c.
+Need to refactor qemu driver to use this shared function.
+*/
+int virProcessGetSchedInfo(unsigned long long *cpuWait, pid_t pid, pid_t tid)
+{
+    g_autofree char *proc = NULL;
+    g_autofree char *data = NULL;
+    char **lines = NULL;
+    size_t i;
+    int ret = -1;
+    double val;
+
+    *cpuWait = 0;
+
+    /* In general, we cannot assume pid_t fits in int; but /proc parsing
+     * is specific to Linux where int works fine.  */
+    if (tid)
+        proc = g_strdup_printf("/proc/%d/task/%d/sched", (int)pid, (int)tid);
+    else
+        proc = g_strdup_printf("/proc/%d/sched", (int)pid);
+    if (!proc)
+        goto cleanup;
+    ret = -1;
+
+    /* The file is not guaranteed to exist (needs CONFIG_SCHED_DEBUG) */
+    if (access(proc, R_OK) < 0) {
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (virFileReadAll(proc, (1<<16), &data) < 0)
+        goto cleanup;
+
+    lines = g_strsplit(data, "\n", 0);
+    if (!lines)
+        goto cleanup;
+
+    for (i = 0; lines[i] != NULL; i++) {
+        const char *line = lines[i];
+
+        /* Needs CONFIG_SCHEDSTATS. The second check
+         * is the old name the kernel used in past */
+        if (STRPREFIX(line, "se.statistics.wait_sum") ||
+            STRPREFIX(line, "se.wait_sum")) {
+            line = strchr(line, ':');
+            if (!line) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Missing separator in sched info '%s'"),
+                               lines[i]);
+                goto cleanup;
+            }
+            line++;
+            while (*line == ' ')
+                line++;
+
+            if (virStrToDouble(line, NULL, &val) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Unable to parse sched info value '%s'"),
+                               line);
+                goto cleanup;
+            }
+
+            *cpuWait = (unsigned long long)(val * 1000000);
+            break;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    g_strfreev(lines);
+    return ret;
+}
diff --git a/src/util/virprocess.h b/src/util/virprocess.h
index 9910331a0c..3a7c4c2d64 100644
--- a/src/util/virprocess.h
+++ b/src/util/virprocess.h
@@ -127,3 +127,8 @@ typedef enum {
 } virProcessNamespaceFlags;
 
 int virProcessNamespaceAvailable(unsigned int ns);
+
+int virProcessGetStatInfo(unsigned long long *cpuTime, int *lastCpu,
+                          long *vm_rss, pid_t pid, pid_t tid);
+int virProcessGetSchedInfo(unsigned long long *cpuWait, pid_t pid,
+                           pid_t tid);
-- 
2.27.0


Re: [libvirt PATCH 01/13] util: Helper functions to get process info
Posted by Daniel Henrique Barboza 4 years, 2 months ago

On 10/21/21 16:31, Vineeth Pillai wrote:
> These helper methods are used to capture vcpu information
> in ch driver.
> 
> Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
> Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> ---
>   src/util/virprocess.c | 136 ++++++++++++++++++++++++++++++++++++++++++
>   src/util/virprocess.h |   5 ++
>   2 files changed, 141 insertions(+)
> 
> diff --git a/src/util/virprocess.c b/src/util/virprocess.c
> index 6de3f36f52..0164d70df6 100644
> --- a/src/util/virprocess.c
> +++ b/src/util/virprocess.c
> @@ -1721,3 +1721,139 @@ virProcessSetScheduler(pid_t pid G_GNUC_UNUSED,
>   }
>   
>   #endif /* !WITH_SCHED_SETSCHEDULER */
> +
> +/*
> +TODO: This method was cloned from qemuGetProcessInfo in src/qemu/qemu_driver.c.
> +Need to refactor qemu driver to use this shared function.
> +*/

Instead of cloning and leaving a TODO just go ahead and do the change in the
qemu_driver.c as well.

You don't need to change every instance of qemuGetProcessInfo in QEMU files
to use this new helper. Just change qemuGetProcessInfo to use
virProcessGetStatInfo:


static int
qemuGetProcessInfo(unsigned long long *cpuTime, int *lastCpu, long *vm_rss,
                    pid_t pid, int tid)
{
     return virProcessGetStatInfo(cpuTime, lastCpu, vm_rss, pid, tid);
}


Same thing down below with qemuGetSchedInfo and virProcessGetSchedInfo.


Thanks,


Daniel


> +int
> +virProcessGetStatInfo(unsigned long long *cpuTime, int *lastCpu, long *vm_rss,
> +                   pid_t pid, pid_t tid)
> +{
> +    g_autofree char *proc = NULL;
> +    FILE *pidinfo;
> +    unsigned long long usertime = 0, systime = 0;
> +    long rss = 0;
> +    int cpu = 0;
> +
> +    /* In general, we cannot assume pid_t fits in int; but /proc parsing
> +     * is specific to Linux where int works fine.  */
> +    if (tid)
> +        proc = g_strdup_printf("/proc/%d/task/%d/stat", (int)pid, tid);
> +    else
> +        proc = g_strdup_printf("/proc/%d/stat", (int)pid);
> +    if (!proc)
> +        return -1;
> +
> +    pidinfo = fopen(proc, "r");
> +
> +    /* See 'man proc' for information about what all these fields are. We're
> +     * only interested in a very few of them */
> +    if (!pidinfo ||
> +        fscanf(pidinfo,
> +               /* pid -> stime */
> +               "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu"
> +               /* cutime -> endcode */
> +               "%*d %*d %*d %*d %*d %*d %*u %*u %ld %*u %*u %*u"
> +               /* startstack -> processor */
> +               "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %d",
> +               &usertime, &systime, &rss, &cpu) != 4) {
> +        VIR_WARN("cannot parse process status data");
> +    }
> +
> +    /* We got jiffies
> +     * We want nanoseconds
> +     * _SC_CLK_TCK is jiffies per second
> +     * So calculate thus....
> +     */
> +    if (cpuTime)
> +        *cpuTime = 1000ull * 1000ull * 1000ull * (usertime + systime)
> +            / (unsigned long long)sysconf(_SC_CLK_TCK);
> +    if (lastCpu)
> +        *lastCpu = cpu;
> +
> +    if (vm_rss)
> +        *vm_rss = rss * virGetSystemPageSizeKB();
> +
> +
> +    VIR_DEBUG("Got status for %d/%d user=%llu sys=%llu cpu=%d rss=%ld",
> +              (int)pid, tid, usertime, systime, cpu, rss);
> +
> +    VIR_FORCE_FCLOSE(pidinfo);
> +
> +    return 0;
> +}
> +/*
> +TODO: This method was cloned from qemuGetSchedInfo in src/qemu/qemu_driver.c.
> +Need to refactor qemu driver to use this shared function.
> +*/
> +int virProcessGetSchedInfo(unsigned long long *cpuWait, pid_t pid, pid_t tid)
> +{
> +    g_autofree char *proc = NULL;
> +    g_autofree char *data = NULL;
> +    char **lines = NULL;
> +    size_t i;
> +    int ret = -1;
> +    double val;
> +
> +    *cpuWait = 0;
> +
> +    /* In general, we cannot assume pid_t fits in int; but /proc parsing
> +     * is specific to Linux where int works fine.  */
> +    if (tid)
> +        proc = g_strdup_printf("/proc/%d/task/%d/sched", (int)pid, (int)tid);
> +    else
> +        proc = g_strdup_printf("/proc/%d/sched", (int)pid);
> +    if (!proc)
> +        goto cleanup;
> +    ret = -1;
> +
> +    /* The file is not guaranteed to exist (needs CONFIG_SCHED_DEBUG) */
> +    if (access(proc, R_OK) < 0) {
> +        ret = 0;
> +        goto cleanup;
> +    }
> +
> +    if (virFileReadAll(proc, (1<<16), &data) < 0)
> +        goto cleanup;
> +
> +    lines = g_strsplit(data, "\n", 0);
> +    if (!lines)
> +        goto cleanup;
> +
> +    for (i = 0; lines[i] != NULL; i++) {
> +        const char *line = lines[i];
> +
> +        /* Needs CONFIG_SCHEDSTATS. The second check
> +         * is the old name the kernel used in past */
> +        if (STRPREFIX(line, "se.statistics.wait_sum") ||
> +            STRPREFIX(line, "se.wait_sum")) {
> +            line = strchr(line, ':');
> +            if (!line) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("Missing separator in sched info '%s'"),
> +                               lines[i]);
> +                goto cleanup;
> +            }
> +            line++;
> +            while (*line == ' ')
> +                line++;
> +
> +            if (virStrToDouble(line, NULL, &val) < 0) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("Unable to parse sched info value '%s'"),
> +                               line);
> +                goto cleanup;
> +            }
> +
> +            *cpuWait = (unsigned long long)(val * 1000000);
> +            break;
> +        }
> +    }
> +
> +    ret = 0;
> +
> + cleanup:
> +    g_strfreev(lines);
> +    return ret;
> +}
> diff --git a/src/util/virprocess.h b/src/util/virprocess.h
> index 9910331a0c..3a7c4c2d64 100644
> --- a/src/util/virprocess.h
> +++ b/src/util/virprocess.h
> @@ -127,3 +127,8 @@ typedef enum {
>   } virProcessNamespaceFlags;
>   
>   int virProcessNamespaceAvailable(unsigned int ns);
> +
> +int virProcessGetStatInfo(unsigned long long *cpuTime, int *lastCpu,
> +                          long *vm_rss, pid_t pid, pid_t tid);
> +int virProcessGetSchedInfo(unsigned long long *cpuWait, pid_t pid,
> +                           pid_t tid);
>