virt-top: correct "virt-top -1" command via cpuacct cgroup
authorLai Jiangshan <laijs@cn.fujitsu.com>
Wed, 8 Feb 2012 08:59:50 +0000 (16:59 +0800)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 6 Mar 2012 11:33:13 +0000 (11:33 +0000)
Old "virt-top -1" is not correct, its output is generated by guess:
use average usage for pinned physical CPUs.

example(old "virt-top -1"):

PHYCPU %CPU rhel6  Windows
   0    0.6  0.1=   0.5=
   1    0.6  0.1=   0.5=#
   2    0.6  0.1=   0.5=
   3    0.6  0.1=#  0.5=

The output almost makes no sense(all the value are just average, not real).

This is new implement, it use cpuacct cgroup to gain *real* physical usages
via cpuacct cgroup by virDomainGetCPUStats() API.

new result:

PHYCPU %CPU rhel6  Windows
   0    1.3  0.3     1.0
   1    2.3  0.3     2.0
   2    2.2  0.5     1.7
   3    2.5  0.4     2.1

PHYCPU %CPU rhel6  Windows
   0    1.7  0.4     1.3
   1    3.6  1.0     2.7
   2    1.6  0.4     1.2
   3    4.8  3.1     1.7

Note: average flag(=) is dropped, there is not average value in here.
Note: running flag(#) is dropped, because if the value is not empty,
      it means the guest was once running in the physical CPU in this period
      between updates.

Changed from V3:
use new virDomainGetCPUStats() libvirt-API.
add a new function find_usages_from_stats() to gain cpu usages.

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
README
virt-top/virt_top.ml

diff --git a/README b/README
index a3ef5dc..b91145b 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 virt-top
 ----------------------------------------------------------------------
 
-Copyright (C) 2007-2011 Richard W.M. Jones, Red Hat Inc.
+Copyright (C) 2007-2012 Richard W.M. Jones, Red Hat Inc.
 http://et.redhat.com/~rjones/virt-top/
 http://libvirt.org/ocaml/
 http://libvirt.org/
index ef5ac67..e2fe554 100644 (file)
@@ -446,14 +446,14 @@ let collect, clear_pcpu_display_data =
   let last_info = Hashtbl.create 13 in
   let last_time = ref (Unix.gettimeofday ()) in
 
-  (* Save vcpuinfo structures across redraws too (only for pCPU display). *)
-  let last_vcpu_info = Hashtbl.create 13 in
+  (* Save pcpu_usages structures across redraws too (only for pCPU display). *)
+  let last_pcpu_usages = Hashtbl.create 13 in
 
   let clear_pcpu_display_data () =
-    (* Clear out vcpu_info used by PCPUDisplay display_mode
+    (* Clear out pcpu_usages used by PCPUDisplay display_mode
      * when we switch back to TaskDisplay mode.
      *)
-    Hashtbl.clear last_vcpu_info
+    Hashtbl.clear last_pcpu_usages
   in
 
   let collect (conn, _, _, _, _, node_info, _, _) =
@@ -652,22 +652,28 @@ let collect, clear_pcpu_display_data =
              (try
                 let domid = rd.rd_domid in
                 let maplen = C.cpumaplen nr_pcpus in
+                let cpu_stats = D.get_cpu_stats rd.rd_dom nr_pcpus in
+                let rec find_usages_from_stats = function
+                  | ("cpu_time", D.TypedFieldUInt64 usages) :: _ -> usages
+                  | _ :: params -> find_usages_from_stats params
+                  | [] -> 0L in
+                let pcpu_usages = Array.map find_usages_from_stats cpu_stats in
                 let maxinfo = rd.rd_info.D.nr_virt_cpu in
                 let nr_vcpus, vcpu_infos, cpumaps =
                   D.get_vcpus rd.rd_dom maxinfo maplen in
 
-                (* Got previous vcpu_infos for this domain? *)
-                let prev_vcpu_infos =
-                  try Some (Hashtbl.find last_vcpu_info domid)
+                (* Got previous pcpu_usages for this domain? *)
+                let prev_pcpu_usages =
+                  try Some (Hashtbl.find last_pcpu_usages domid)
                   with Not_found -> None in
-                (* Update last_vcpu_info. *)
-                Hashtbl.replace last_vcpu_info domid vcpu_infos;
-
-                (match prev_vcpu_infos with
-                 | Some prev_vcpu_infos
-                     when Array.length prev_vcpu_infos = Array.length vcpu_infos ->
-                     Some (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos,
-                           cpumaps, maplen)
+                (* Update last_pcpu_usages. *)
+                Hashtbl.replace last_pcpu_usages domid pcpu_usages;
+
+                (match prev_pcpu_usages with
+                 | Some prev_pcpu_usages
+                     when Array.length prev_pcpu_usages = Array.length pcpu_usages ->
+                     Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages,
+                           prev_pcpu_usages, cpumaps, maplen)
                  | _ -> None (* ignore missing / unequal length prev_vcpu_infos *)
                 );
               with
@@ -680,37 +686,15 @@ let collect, clear_pcpu_display_data =
        (* Rearrange the data into a matrix.  Major axis (down) is
         * pCPUs.  Minor axis (right) is domains.  At each node we store:
         *  cpu_time (on this pCPU only, nanosecs),
-        *  average? (if set, then cpu_time is an average because the
-        *     vCPU is pinned to more than one pCPU)
-        *  running? (if set, we were instantaneously running on this pCPU)
         *)
-       let empty_node = (0L, false, false) in
-       let pcpus = Array.make_matrix nr_pcpus nr_doms empty_node in
+       let pcpus = Array.make_matrix nr_pcpus nr_doms 0L in
 
        List.iteri (
-         fun di (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos,
-                 cpumaps, maplen) ->
+         fun di (domid, name, nr_vcpus, vcpu_infos, pcpu_usages,
+                 prev_pcpu_usages, cpumaps, maplen) ->
            (* Which pCPUs can this dom run on? *)
-           for v = 0 to nr_vcpus-1 do
-             let pcpu = vcpu_infos.(v).D.cpu in (* instantaneous pCPU *)
-             let nr_poss_pcpus = ref 0 in (* how many pcpus can it run on? *)
-             for p = 0 to nr_pcpus-1 do
-               (* vcpu v can reside on pcpu p *)
-               if C.cpu_usable cpumaps maplen v p then
-                 incr nr_poss_pcpus
-             done;
-             let nr_poss_pcpus = Int64.of_int !nr_poss_pcpus in
-             for p = 0 to nr_pcpus-1 do
-               (* vcpu v can reside on pcpu p *)
-               if C.cpu_usable cpumaps maplen v p then
-                 let vcpu_time_on_pcpu =
-                   vcpu_infos.(v).D.vcpu_time
-                   -^ prev_vcpu_infos.(v).D.vcpu_time in
-                 let vcpu_time_on_pcpu =
-                   vcpu_time_on_pcpu /^ nr_poss_pcpus in
-                 pcpus.(p).(di) <-
-                   (vcpu_time_on_pcpu, nr_poss_pcpus > 1L, p = pcpu)
-             done
+           for p = 0 to Array.length pcpu_usages - 1 do
+             pcpus.(p).(di) <- pcpu_usages.(p) -^ prev_pcpu_usages.(p)
            done
        ) doms;
 
@@ -719,7 +703,7 @@ let collect, clear_pcpu_display_data =
          fun row ->
            let cpu_time = ref 0L in
            for di = 0 to Array.length row-1 do
-             let t, _, _ = row.(di) in
+             let t = row.(di) in
              cpu_time := !cpu_time +^ t
            done;
            Int64.to_float !cpu_time
@@ -938,7 +922,7 @@ let redraw =
         let dom_names =
           String.concat "" (
             List.map (
-              fun (_, name, _, _, _, _, _) ->
+              fun (_, name, _, _, _, _, _, _) ->
                 let len = String.length name in
                 let width = max (len+1) 7 in
                 pad width name
@@ -957,8 +941,8 @@ let redraw =
             addch ' ';
 
             List.iteri (
-              fun di (domid, name, _, _, _, _, _) ->
-                let t, is_average, is_running = pcpus.(p).(di) in
+              fun di (domid, name, _, _, _, _, _, _) ->
+                let t = pcpus.(p).(di) in
                 let len = String.length name in
                 let width = max (len+1) 7 in
                 let str =
@@ -966,9 +950,7 @@ let redraw =
                   else (
                     let t = Int64.to_float t in
                     let percent = 100. *. t /. total_cpu_per_pcpu in
-                    sprintf "%s%c%c " (Show.percent percent)
-                      (if is_average then '=' else ' ')
-                      (if is_running then '#' else ' ')
+                    sprintf "%s " (Show.percent percent)
                   ) in
                 addstr (pad width str);
                 ()