Better error messages when parsing the init file (RHBZ#836231).
[virt-top.git] / virt-top / virt_top.ml
index 75042c1..7e4677c 100644 (file)
@@ -126,11 +126,13 @@ let uri = ref None
 let debug_file = ref ""
 let csv_enabled = ref false
 let csv_cpu = ref true
+let csv_mem = ref true
 let csv_block = ref true
 let csv_net = ref true
 let init_file = ref DefaultInitFile
 let script_mode = ref false
 let stream_mode = ref false
+let block_in_bytes = ref false
 
 (* Tuple of never-changing data returned by start_up function. *)
 type setup =
@@ -170,13 +172,15 @@ let start_up () =
     "-b", Arg.Set batch_mode,
       " " ^ s_"Batch mode";
     "-c", Arg.String set_uri,
-      "uri " ^ s_"Connect to URI (default: Xen)";
+      "uri " ^ s_"Connect to libvirt URI";
     "--connect", Arg.String set_uri,
-      "uri " ^ s_"Connect to URI (default: Xen)";
+      "uri " ^ s_"Connect to libvirt URI";
     "--csv", Arg.String set_csv,
       "file " ^ s_"Log statistics to CSV file";
     "--no-csv-cpu", Arg.Clear csv_cpu,
       " " ^ s_"Disable CPU stats in CSV";
+    "--no-csv-mem", Arg.Clear csv_mem,
+      " " ^ s_"Disable memory stats in CSV";
     "--no-csv-block", Arg.Clear csv_block,
       " " ^ s_"Disable block device stats in CSV";
     "--no-csv-net", Arg.Clear csv_net,
@@ -196,13 +200,16 @@ let start_up () =
     "-n", Arg.Set_int iterations,
       "iterations " ^ s_"Number of iterations to run";
     "-o", Arg.String set_sort,
-      "sort " ^ sprintf (f_"Set sort order (%s)") "cpu|mem|time|id|name";
+      "sort " ^ sprintf (f_"Set sort order (%s)")
+        "cpu|mem|time|id|name|netrx|nettx|blockrdrq|blockwrrq";
     "-s", Arg.Set secure_mode,
       " " ^ s_"Secure (\"kiosk\") mode";
     "--script", Arg.Set script_mode,
       " " ^ s_"Run from a script (no user interface)";
     "--stream", Arg.Set stream_mode,
       " " ^ s_"dump output to stdout (no userinterface)";
+    "--block-in-bytes", Arg.Set block_in_bytes,
+      " " ^ s_"show block device load in bytes rather than reqs";
     "--version", Arg.Unit display_version,
       " " ^ s_"Display version number and exit";
   ] in
@@ -219,6 +226,24 @@ OPTIONS" in
   (* Read the init file. *)
   let try_to_read_init_file filename =
     let config = read_config_file filename in
+    (* Replacement functions that raise better errors when
+     * parsing the init file.
+     *)
+    let int_of_string s =
+      try int_of_string s
+      with Invalid_argument _ ->
+        failwithf (f_"%s: could not parse '%s' in init file: expecting an integer")
+          filename s in
+    let float_of_string s =
+      try float_of_string s
+      with Invalid_argument _ ->
+        failwithf (f_"%s: could not parse '%s' in init file: expecting a number")
+          filename s in
+    let bool_of_string s =
+      try bool_of_string s
+      with Invalid_argument _ ->
+        failwithf (f_"%s: could not parse '%s' in init file: expecting %s")
+          filename s "true|false" in
     List.iter (
       function
       | _, "display", mode -> display_mode := display_of_cli mode
@@ -230,12 +255,14 @@ OPTIONS" in
       | _, "debug", filename -> debug_file := filename
       | _, "csv", filename -> set_csv filename
       | _, "csv-cpu", b -> csv_cpu := bool_of_string b
+      | _, "csv-mem", b -> csv_mem := bool_of_string b
       | _, "csv-block", b -> csv_block := bool_of_string b
       | _, "csv-net", b -> csv_net := bool_of_string b
       | _, "batch", b -> batch_mode := bool_of_string b
       | _, "secure", b -> secure_mode := bool_of_string b
       | _, "script", b -> script_mode := bool_of_string b
       | _, "stream", b -> stream_mode := bool_of_string b
+      | _, "block-in-bytes", b -> block_in_bytes := bool_of_string b
       | _, "end-time", t -> set_end_time t
       | _, "overwrite-init-file", "false" -> no_init_file ()
       | lineno, key, _ ->
@@ -264,7 +291,7 @@ OPTIONS" in
        prerr_endline (Libvirt.Virterror.to_string err);
        (* If non-root and no explicit connection URI, print a warning. *)
        if Unix.geteuid () <> 0 && name = None then (
-         print_endline (s_"NB: If you want to monitor a local Xen hypervisor, you usually need to be root");
+         print_endline (s_"NB: If you want to monitor a local hypervisor, you usually need to be root");
        );
        exit 1 in
 
@@ -398,9 +425,17 @@ and rd_active = {
   (* The following are since the last slice, or 0 if cannot be calculated: *)
   rd_cpu_time : float;                 (* CPU time used in nanoseconds. *)
   rd_percent_cpu : float;              (* CPU time as percent of total. *)
+  rd_mem_bytes : int64;                        (* Memory usage in bytes *)
+  rd_mem_percent: int64;               (* Memory usage as percent of total *)
   (* The following are since the last slice, or None if cannot be calc'd: *)
   rd_block_rd_reqs : int64 option;      (* Number of block device read rqs. *)
   rd_block_wr_reqs : int64 option;      (* Number of block device write rqs. *)
+  rd_block_rd_bytes : int64 option;   (* Number of bytes block device read *)
+  rd_block_wr_bytes : int64 option;   (* Number of bytes block device write *)
+  (* _info fields includes the number considering --block_in_bytes option *)
+  rd_block_rd_info : int64 option;    (* Block device read info for user *)
+  rd_block_wr_info : int64 option;    (* Block device read info for user *)
+
   rd_net_rx_bytes : int64 option;      (* Number of bytes received. *)
   rd_net_tx_bytes : int64 option;      (* Number of bytes transmitted. *)
 }
@@ -430,14 +465,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, _, _) =
@@ -500,7 +535,10 @@ let collect, clear_pcpu_display_data =
                      rd_prev_block_stats = prev_block_stats;
                      rd_prev_interface_stats = prev_interface_stats;
                      rd_cpu_time = 0.; rd_percent_cpu = 0.;
+                      rd_mem_bytes = 0L; rd_mem_percent = 0L;
                      rd_block_rd_reqs = None; rd_block_wr_reqs = None;
+                      rd_block_rd_bytes = None; rd_block_wr_bytes = None;
+                      rd_block_rd_info = None; rd_block_wr_info = None;
                      rd_net_rx_bytes = None; rd_net_tx_bytes = None;
                    })
            with
@@ -532,9 +570,14 @@ let collect, clear_pcpu_display_data =
            let cpu_time =
              Int64.to_float (rd.rd_info.D.cpu_time -^ prev_info.D.cpu_time) in
            let percent_cpu = 100. *. cpu_time /. total_cpu in
+            let mem_usage = rd.rd_info.D.memory in
+            let mem_percent =
+                100L *^ rd.rd_info.D.memory /^ node_info.C.memory in
            let rd = { rd with
                         rd_cpu_time = cpu_time;
-                        rd_percent_cpu = percent_cpu } in
+                        rd_percent_cpu = percent_cpu;
+                        rd_mem_bytes = mem_usage;
+                         rd_mem_percent = mem_percent} in
            name, Active rd
        (* For all other domains we can't calculate it, so leave as 0 *)
        | rd -> rd
@@ -562,10 +605,23 @@ let collect, clear_pcpu_display_data =
              block_stats.D.rd_req -^ prev_block_stats.D.rd_req in
            let write_reqs =
              block_stats.D.wr_req -^ prev_block_stats.D.wr_req in
+            let read_bytes =
+              block_stats.D.rd_bytes -^ prev_block_stats.D.rd_bytes in
+            let write_bytes =
+              block_stats.D.wr_bytes -^ prev_block_stats.D.wr_bytes in
 
            let rd = { rd with
                         rd_block_rd_reqs = Some read_reqs;
-                        rd_block_wr_reqs = Some write_reqs } in
+                        rd_block_wr_reqs = Some write_reqs;
+                         rd_block_rd_bytes = Some read_bytes;
+                         rd_block_wr_bytes = Some write_bytes;
+            } in
+            let rd = { rd with
+                         rd_block_rd_info = if !block_in_bytes then
+                         rd.rd_block_rd_bytes else rd.rd_block_rd_reqs;
+                         rd_block_wr_info = if !block_in_bytes then
+                         rd.rd_block_wr_bytes else rd.rd_block_wr_reqs;
+            } in
            name, Active rd
        (* For all other domains we can't calculate it, so leave as None. *)
        | rd -> rd
@@ -615,22 +671,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
@@ -643,37 +705,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;
 
@@ -682,7 +722,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
@@ -849,8 +889,12 @@ let redraw =
 
         (* Print domains. *)
         attron A.reverse;
-        mvaddstr header_lineno 0
-          (pad cols "   ID S RDRQ WRRQ RXBY TXBY %CPU %MEM    TIME   NAME");
+         let header_string = if !block_in_bytes
+         then "   ID S RDBY WRBY RXBY TXBY %CPU %MEM    TIME   NAME"
+         else "   ID S RDRQ WRRQ RXBY TXBY %CPU %MEM    TIME   NAME"
+         in
+          mvaddstr header_lineno 0
+           (pad cols header_string);
         attroff A.reverse;
 
         let rec loop lineno = function
@@ -858,14 +902,12 @@ let redraw =
           | (name, Active rd) :: doms ->
               if lineno < lines then (
                 let state = show_state rd.rd_info.D.state in
-                let rd_req = Show.int64_option rd.rd_block_rd_reqs in
-                let wr_req = Show.int64_option rd.rd_block_wr_reqs in
+                let rd_req = Show.int64_option rd.rd_block_rd_info in
+                let wr_req = Show.int64_option rd.rd_block_wr_info in
                 let rx_bytes = Show.int64_option rd.rd_net_rx_bytes in
                 let tx_bytes = Show.int64_option rd.rd_net_tx_bytes in
                 let percent_cpu = Show.percent rd.rd_percent_cpu in
-                let percent_mem =
-                  100L *^ rd.rd_info.D.memory /^ node_info.C.memory in
-                let percent_mem = Int64.to_float percent_mem in
+                let percent_mem = Int64.to_float rd.rd_mem_percent in
                 let percent_mem = Show.percent percent_mem in
                 let time = Show.time rd.rd_info.D.cpu_time in
 
@@ -899,7 +941,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
@@ -918,8 +960,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 =
@@ -927,9 +969,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);
                 ()
@@ -1203,7 +1243,11 @@ let write_csv_header () =
       (* These fields are repeated for each domain: *)
     [ "Domain ID"; "Domain name"; ] @
     (if !csv_cpu then [ "CPU (ns)"; "%CPU"; ] else []) @
-    (if !csv_block then [ "Block RDRQ"; "Block WRRQ"; ] else []) @
+    (if !csv_mem then [ "Mem (bytes)"; "%Mem";] else []) @
+    (if !csv_block && not !block_in_bytes
+       then [ "Block RDRQ"; "Block WRRQ"; ] else []) @
+    (if !csv_block && !block_in_bytes
+       then [ "Block RDBY"; "Block WRBY"; ] else []) @
     (if !csv_net then [ "Net RXBY"; "Net TXBY" ] else [])
   )
 
@@ -1257,9 +1301,12 @@ let append_csv
        (if !csv_cpu then [
           string_of_float rd.rd_cpu_time; string_of_float rd.rd_percent_cpu
         ] else []) @
+        (if !csv_mem then [
+            Int64.to_string rd.rd_mem_bytes; Int64.to_string rd.rd_mem_percent
+         ] else []) @
        (if !csv_block then [
-          string_of_int64_option rd.rd_block_rd_reqs;
-          string_of_int64_option rd.rd_block_wr_reqs;
+          string_of_int64_option rd.rd_block_rd_info;
+          string_of_int64_option rd.rd_block_wr_info;
         ] else []) @
        (if !csv_net then [
           string_of_int64_option rd.rd_net_rx_bytes;
@@ -1283,7 +1330,10 @@ let dump_stdout
     printable_time hostname node_info.C.model node_info.C.cpus nr_pcpus
     node_info.C.mhz (node_info.C.memory /^ 1024L);
   (* dump domain information one by one *)
-  printf "   ID S RDRQ WRRQ RXBY TXBY %%CPU %%MEM   TIME    NAME\n";
+   let rd, wr = if !block_in_bytes then "RDBY", "WRBY" else "RDRQ", "WRRQ"
+   in
+     printf "   ID S %s %s RXBY TXBY %%CPU %%MEM   TIME    NAME\n" rd wr;
+
   (* sort by ID *)
   let doms =
     let compare =
@@ -1300,18 +1350,16 @@ let dump_stdout
   let dump_domain = fun name rd
   -> begin
     let state = show_state rd.rd_info.D.state in
-    let rd_req = if rd.rd_block_rd_reqs = None then "   0"
-    else Show.int64_option rd.rd_block_rd_reqs in
-    let wr_req = if rd.rd_block_wr_reqs = None then "   0"
-    else Show.int64_option rd.rd_block_wr_reqs in
+         let rd_req = if rd.rd_block_rd_info = None then "   0"
+                      else Show.int64_option rd.rd_block_rd_info in
+         let wr_req = if rd.rd_block_wr_info = None then "   0"
+                      else Show.int64_option rd.rd_block_wr_info in
     let rx_bytes = if rd.rd_net_rx_bytes = None then "   0"
     else Show.int64_option rd.rd_net_rx_bytes in
     let tx_bytes = if rd.rd_net_tx_bytes = None then "   0"
     else Show.int64_option rd.rd_net_tx_bytes in
     let percent_cpu = Show.percent rd.rd_percent_cpu in
-    let percent_mem =
-      100L *^ rd.rd_info.D.memory /^ node_info.C.memory in
-    let percent_mem = Int64.to_float percent_mem in
+    let percent_mem = Int64.to_float rd.rd_mem_percent in
     let percent_mem = Show.percent percent_mem in
     let time = Show.time rd.rd_info.D.cpu_time in
     printf "%5d %c %s %s %s %s %s %s %s %s\n"
@@ -1401,6 +1449,7 @@ and get_key_press setup delay =
     else if k = Char.code '2' then toggle_net_display ()
     else if k = Char.code '3' then toggle_block_display ()
     else if k = Char.code 'W' then write_init_file ()
+    else if k = Char.code 'B' then toggle_block_in_bytes_mode ()
     else unknown_command k
   )
 
@@ -1546,6 +1595,12 @@ and toggle_block_display () =            (* key 3 *)
     | TaskDisplay | NetDisplay -> BlockDisplay
     | BlockDisplay -> TaskDisplay
 
+and toggle_block_in_bytes_mode () =      (* key B *)
+  block_in_bytes :=
+    match !block_in_bytes with
+    | false -> true
+    | true  -> false
+
 (* Write an init file. *)
 and write_init_file () =
   match !init_file with
@@ -1674,6 +1729,7 @@ and show_help (_, _, _, _, _, _, hostname,
   key "q"        (s_"Quit");
   key "d s"      (s_"Set update interval");
   key "h"        (s_"Help");
+  key "B"        (s_"toggle block info req/bytes");
 
   (* Sort order. *)
   ignore (get_lineno ());