Blame SOURCES/0013-Split-up-huge-Top-module-into-smaller-modules.patch

985088
From 90d14bc151e488972d33eefaac2242d9a6e07578 Mon Sep 17 00:00:00 2001
985088
From: "Richard W.M. Jones" <rjones@redhat.com>
985088
Date: Mon, 27 Mar 2017 15:29:23 +0100
985088
Subject: [PATCH 13/17] Split up huge Top module into smaller modules.
985088
985088
This change is hopefully pure refactoring, splitting up the very large
985088
and highly interlinked module into more manageable modules with
985088
well-defined (or at least *better*-defined) interfaces between them.
985088
---
985088
 MANIFEST              |   12 +
985088
 po/POTFILES           |    6 +
985088
 src/.depend           |   36 +-
985088
 src/Makefile.in       |    6 +
985088
 src/README            |   38 +-
985088
 src/collect.ml        |  455 ++++++++++++++++++++
985088
 src/collect.mli       |   86 ++++
985088
 src/csv_output.ml     |  118 +++++
985088
 src/csv_output.mli    |   27 ++
985088
 src/opt_csv.ml        |    2 +-
985088
 src/opt_xml.ml        |    2 +-
985088
 src/redraw.ml         |  506 ++++++++++++++++++++++
985088
 src/redraw.mli        |   20 +
985088
 src/screen.ml         |   52 +++
985088
 src/screen.mli        |   41 ++
985088
 src/stream_output.ml  |   84 ++++
985088
 src/stream_output.mli |   22 +
985088
 src/top.ml            | 1139 ++-----------------------------------------------
985088
 src/top.mli           |   20 +-
985088
 src/types.ml          |  147 +++++++
985088
 src/types.mli         |   49 +++
985088
 src/utils.ml          |   65 ---
985088
 src/utils.mli         |    9 -
985088
 23 files changed, 1719 insertions(+), 1223 deletions(-)
985088
 create mode 100644 src/collect.ml
985088
 create mode 100644 src/collect.mli
985088
 create mode 100644 src/csv_output.ml
985088
 create mode 100644 src/csv_output.mli
985088
 create mode 100644 src/redraw.ml
985088
 create mode 100644 src/redraw.mli
985088
 create mode 100644 src/screen.ml
985088
 create mode 100644 src/screen.mli
985088
 create mode 100644 src/stream_output.ml
985088
 create mode 100644 src/stream_output.mli
985088
 create mode 100644 src/types.ml
985088
 create mode 100644 src/types.mli
985088
985088
diff --git a/MANIFEST b/MANIFEST
985088
index 26e87b2..4e4014b 100644
985088
--- a/MANIFEST
985088
+++ b/MANIFEST
985088
@@ -54,12 +54,24 @@ TODO
985088
 src/.depend
985088
 src/Makefile.in
985088
 src/README
985088
+src/collect.ml
985088
+src/collect.mli
985088
+src/csv_output.ml
985088
+src/csv_output.mli
985088
 src/main.ml
985088
 src/opt_calendar.ml
985088
 src/opt_csv.ml
985088
 src/opt_xml.ml
985088
+src/redraw.ml
985088
+src/redraw.mli
985088
+src/screen.ml
985088
+src/screen.mli
985088
+src/stream_output.ml
985088
+src/stream_output.mli
985088
 src/top.ml
985088
 src/top.mli
985088
+src/types.ml
985088
+src/types.mli
985088
 src/utils.ml
985088
 src/utils.mli
985088
 src/version.ml.in
985088
diff --git a/po/POTFILES b/po/POTFILES
985088
index b826a2a..6150703 100644
985088
--- a/po/POTFILES
985088
+++ b/po/POTFILES
985088
@@ -1,8 +1,14 @@
985088
+../src/collect.ml
985088
+../src/csv_output.ml
985088
 ../src/main.ml
985088
 ../src/opt_calendar.ml
985088
 ../src/opt_csv.ml
985088
 ../src/opt_gettext.ml
985088
 ../src/opt_xml.ml
985088
+../src/redraw.ml
985088
+../src/screen.ml
985088
+../src/stream.ml
985088
 ../src/top.ml
985088
+../src/types.ml
985088
 ../src/utils.ml
985088
 ../src/version.ml
985088
diff --git a/src/.depend b/src/.depend
985088
index f487c18..1075f36 100644
985088
--- a/src/.depend
985088
+++ b/src/.depend
985088
@@ -1,18 +1,36 @@
985088
+collect.cmi: types.cmi
985088
+collect.cmo: utils.cmi types.cmi collect.cmi
985088
+collect.cmx: utils.cmx types.cmx collect.cmi
985088
+csv_output.cmi: types.cmi collect.cmi
985088
+csv_output.cmo: collect.cmi csv_output.cmi
985088
+csv_output.cmx: collect.cmx csv_output.cmi
985088
 main.cmo: top.cmi opt_gettext.cmo
985088
 main.cmx: top.cmx opt_gettext.cmx
985088
 opt_calendar.cmo: top.cmi opt_gettext.cmo
985088
 opt_calendar.cmx: top.cmx opt_gettext.cmx
985088
-opt_csv.cmo: top.cmi opt_gettext.cmo
985088
-opt_csv.cmx: top.cmx opt_gettext.cmx
985088
+opt_csv.cmo: top.cmi opt_gettext.cmo csv_output.cmi
985088
+opt_csv.cmx: top.cmx opt_gettext.cmx csv_output.cmx
985088
 opt_gettext.cmo:
985088
 opt_gettext.cmx:
985088
-opt_xml.cmo: top.cmi opt_gettext.cmo
985088
-opt_xml.cmx: top.cmx opt_gettext.cmx
985088
-top.cmi:
985088
-top.cmo: version.cmo utils.cmi opt_gettext.cmo top.cmi
985088
-top.cmx: version.cmx utils.cmx opt_gettext.cmx top.cmi
985088
+opt_xml.cmo: opt_gettext.cmo collect.cmi
985088
+opt_xml.cmx: opt_gettext.cmx collect.cmx
985088
+redraw.cmi: types.cmi collect.cmi
985088
+redraw.cmo: utils.cmi types.cmi screen.cmi opt_gettext.cmo collect.cmi redraw.cmi
985088
+redraw.cmx: utils.cmx types.cmx screen.cmx opt_gettext.cmx collect.cmx redraw.cmi
985088
+screen.cmi:
985088
+screen.cmo: screen.cmi
985088
+screen.cmx: screen.cmi
985088
+stream_output.cmi: types.cmi collect.cmi
985088
+stream_output.cmo: utils.cmi screen.cmi collect.cmi stream_output.cmi
985088
+stream_output.cmx: utils.cmx screen.cmx collect.cmx stream_output.cmi
985088
+top.cmi: types.cmi
985088
+top.cmo: version.cmo utils.cmi types.cmi stream_output.cmi screen.cmi redraw.cmi opt_gettext.cmo csv_output.cmi collect.cmi top.cmi
985088
+top.cmx: version.cmx utils.cmx types.cmx stream_output.cmx screen.cmx redraw.cmx opt_gettext.cmx csv_output.cmx collect.cmx top.cmi
985088
+types.cmi:
985088
+types.cmo: utils.cmi opt_gettext.cmo types.cmi
985088
+types.cmx: utils.cmx opt_gettext.cmx types.cmi
985088
 utils.cmi:
985088
-utils.cmo: opt_gettext.cmo utils.cmi
985088
-utils.cmx: opt_gettext.cmx utils.cmi
985088
+utils.cmo: utils.cmi
985088
+utils.cmx: utils.cmi
985088
 version.cmo:
985088
 version.cmx:
985088
diff --git a/src/Makefile.in b/src/Makefile.in
985088
index ae896cb..64f431e 100644
985088
--- a/src/Makefile.in
985088
+++ b/src/Makefile.in
985088
@@ -42,6 +42,12 @@ OBJS		:= \
985088
 		   version.cmo \
985088
 		   opt_gettext.cmo \
985088
 		   utils.cmo \
985088
+		   types.cmo \
985088
+		   collect.cmo \
985088
+		   screen.cmo \
985088
+		   redraw.cmo \
985088
+		   csv_output.cmo \
985088
+		   stream_output.cmo \
985088
 		   top.cmo
985088
 ifneq ($(OCAML_PKG_xml_light),no)
985088
 OBJS		+= opt_xml.cmo
985088
diff --git a/src/README b/src/README
985088
index 8aa2348..1fd4be3 100644
985088
--- a/src/README
985088
+++ b/src/README
985088
@@ -5,19 +5,37 @@ The code is structured into these files:
985088
     String functions and other small utility functions.  This is
985088
     included directly into virt_top.ml.
985088
 
985088
+  types.mli, types.ml
985088
+
985088
+    Various internally used types and functions operating on those
985088
+    types.
985088
+
985088
+  collect.mli, collect.ml
985088
+
985088
+    Stats information is collected in these functions.
985088
+
985088
+  screen.mli, screen.ml
985088
+
985088
+    Various useful functions for drawing to the curses screen.
985088
+
985088
+  redraw.mli, redraw.ml
985088
+
985088
+    Redraw the main display.
985088
+
985088
+  csv_output.mli, csv_output.ml
985088
+
985088
+    Functions which implement --csv mode.
985088
+
985088
+  stream_output.mli, stream_output.ml
985088
+
985088
+    Functions which implement --stream mode.
985088
+
985088
   top.mli, top.ml
985088
 
985088
     This is the virt-top program.
985088
 
985088
-    The two interesting functions are called 'collect' and 'redraw'.
985088
-
985088
-    'collect' collects all the information about domains, etc.
985088
-
985088
-    'redraw' updates the display on each frame.
985088
-
985088
-    Another interesting function is 'start_up' which handles all
985088
-    start-up stuff, eg. command line arguments, connecting to the
985088
-    hypervisor, enabling curses.
985088
+    'start_up' handles all start-up stuff, eg. command line arguments,
985088
+    connecting to the hypervisor, enabling curses.
985088
 
985088
     The function 'main_loop' runs the main loop and has sub-functions
985088
     to deal with keypresses, help screens and so on.
985088
@@ -38,7 +56,7 @@ The code is structured into these files:
985088
   opt_csv.ml
985088
 
985088
     Any code which needs the optional ocaml-csv library goes
985088
-    in here.  This implements the --csv command line option.
985088
+    in here.
985088
 
985088
   opt_calendar.ml
985088
 
985088
diff --git a/src/collect.ml b/src/collect.ml
985088
new file mode 100644
985088
index 0000000..f856067
985088
--- /dev/null
985088
+++ b/src/collect.ml
985088
@@ -0,0 +1,455 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+module C = Libvirt.Connect
985088
+module D = Libvirt.Domain
985088
+
985088
+open Printf
985088
+open ExtList
985088
+
985088
+open Utils
985088
+open Types
985088
+
985088
+(* Hook for XML support (see [opt_xml.ml]). *)
985088
+let parse_device_xml : (int -> [>`R] D.t -> string list * string list) ref =
985088
+  ref (
985088
+    fun _ _ -> [], []
985088
+  )
985088
+
985088
+(* Intermediate "domain + stats" structure that we use to collect
985088
+ * everything we know about a domain within the collect function.
985088
+ *)
985088
+type rd_domain = Inactive | Active of rd_active
985088
+and rd_active = {
985088
+  rd_domid : int;			(* Domain ID. *)
985088
+  rd_dom : [`R] D.t;			(* Domain object. *)
985088
+  rd_info : D.info;			(* Domain CPU info now. *)
985088
+  rd_block_stats : (string * D.block_stats) list;
985088
+                                        (* Domain block stats now. *)
985088
+  rd_interface_stats : (string * D.interface_stats) list;
985088
+                                        (* Domain net stats now. *)
985088
+  rd_prev_info : D.info option;		(* Domain CPU info previously. *)
985088
+  rd_prev_block_stats : (string * D.block_stats) list;
985088
+                                        (* Domain block stats prev. *)
985088
+  rd_prev_interface_stats : (string * D.interface_stats) list;
985088
+                                        (* Domain interface stats prev. *)
985088
+  (* The following are since the last slice, or 0 if cannot be calculated: *)
985088
+  rd_cpu_time : float;			(* CPU time used in nanoseconds. *)
985088
+  rd_percent_cpu : float;		(* CPU time as percent of total. *)
985088
+  rd_mem_bytes : int64;		        (* Memory usage in bytes *)
985088
+  rd_mem_percent: int64;		(* Memory usage as percent of total *)
985088
+  (* The following are since the last slice, or None if cannot be calc'd: *)
985088
+  rd_block_rd_reqs : int64 option;      (* Number of block device read rqs. *)
985088
+  rd_block_wr_reqs : int64 option;      (* Number of block device write rqs. *)
985088
+  rd_block_rd_bytes : int64 option;   (* Number of bytes block device read *)
985088
+  rd_block_wr_bytes : int64 option;   (* Number of bytes block device write *)
985088
+  (* _info fields includes the number considering --block_in_bytes option *)
985088
+  rd_block_rd_info : int64 option;    (* Block device read info for user *)
985088
+  rd_block_wr_info : int64 option;    (* Block device read info for user *)
985088
+
985088
+  rd_net_rx_bytes : int64 option;	(* Number of bytes received. *)
985088
+  rd_net_tx_bytes : int64 option;	(* Number of bytes transmitted. *)
985088
+}
985088
+
985088
+type stats = {
985088
+  rd_doms : (string * rd_domain) list;  (* List of domains. *)
985088
+  rd_time : float;
985088
+  rd_printable_time : string;
985088
+  rd_nr_pcpus : int;
985088
+  rd_total_cpu : float;
985088
+  rd_total_cpu_per_pcpu : float;
985088
+  rd_totals : (int * int * int * int * int * int * int * int * int * float *
985088
+                 int64 * int64);
985088
+}
985088
+
985088
+type pcpu_stats = {
985088
+  rd_pcpu_doms : (int * string * int *
985088
+                  Libvirt.Domain.vcpu_info array * int64 array array *
985088
+                  int64 array array * string * int) list;
985088
+  rd_pcpu_pcpus : int64 array array array;
985088
+  rd_pcpu_pcpus_cpu_time : float array
985088
+}
985088
+
985088
+(* We cache the list of block devices and interfaces for each domain
985088
+ * here, so we don't need to reparse the XML each time.
985088
+ *)
985088
+let devices = Hashtbl.create 13
985088
+
985088
+(* Function to get the list of block devices, network interfaces for
985088
+ * a particular domain.  Get it from the devices cache, and if not
985088
+ * there then parse the domain XML.
985088
+ *)
985088
+let get_devices id dom =
985088
+  try Hashtbl.find devices id
985088
+  with Not_found ->
985088
+    let blkdevs, netifs = (!parse_device_xml) id dom in
985088
+    Hashtbl.replace devices id (blkdevs, netifs);
985088
+    blkdevs, netifs
985088
+
985088
+(* We save the state of domains across redraws here, which allows us
985088
+ * to deduce %CPU usage from the running total.
985088
+ *)
985088
+let last_info = Hashtbl.create 13
985088
+let last_time = ref (Unix.gettimeofday ())
985088
+
985088
+(* Save pcpu_usages structures across redraws too (only for pCPU display). *)
985088
+let last_pcpu_usages = Hashtbl.create 13
985088
+
985088
+let clear_pcpu_display_data () =
985088
+  Hashtbl.clear last_pcpu_usages
985088
+
985088
+let collect (conn, _, _, _, _, node_info, _, _) block_in_bytes =
985088
+  (* Number of physical CPUs (some may be disabled). *)
985088
+  let nr_pcpus = C.maxcpus_of_node_info node_info in
985088
+
985088
+  (* Get the current time. *)
985088
+  let time = Unix.gettimeofday () in
985088
+  let tm = Unix.localtime time in
985088
+  let printable_time =
985088
+    sprintf "%02d:%02d:%02d" tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec in
985088
+
985088
+  (* What's the total CPU time elapsed since we were last called? (ns) *)
985088
+  let total_cpu_per_pcpu = 1_000_000_000. *. (time -. !last_time) in
985088
+  (* Avoid division by zero. *)
985088
+  let total_cpu_per_pcpu =
985088
+    if total_cpu_per_pcpu <= 0. then 1. else total_cpu_per_pcpu in
985088
+  let total_cpu = float node_info.C.cpus *. total_cpu_per_pcpu in
985088
+
985088
+  (* Get the domains.  Match up with their last_info (if any). *)
985088
+  let doms =
985088
+    (* Active domains. *)
985088
+    let n = C.num_of_domains conn in
985088
+    let ids =
985088
+      if n > 0 then Array.to_list (C.list_domains conn n)
985088
+      else [] in
985088
+    let doms =
985088
+      List.filter_map (
985088
+	fun id ->
985088
+	  try
985088
+	    let dom = D.lookup_by_id conn id in
985088
+	    let name = D.get_name dom in
985088
+	    let blkdevs, netifs = get_devices id dom in
985088
+
985088
+	    (* Get current CPU, block and network stats. *)
985088
+	    let info = D.get_info dom in
985088
+	    let block_stats =
985088
+	      try List.map (fun dev -> dev, D.block_stats dom dev) blkdevs
985088
+	      with
985088
+	      | Libvirt.Not_supported "virDomainBlockStats"
985088
+	      | Libvirt.Virterror _ -> [] in
985088
+	    let interface_stats =
985088
+	      try List.map (fun dev -> dev, D.interface_stats dom dev) netifs
985088
+	      with
985088
+	      | Libvirt.Not_supported "virDomainInterfaceStats"
985088
+	      | Libvirt.Virterror _ -> [] in
985088
+
985088
+	    let prev_info, prev_block_stats, prev_interface_stats =
985088
+	      try
985088
+		let prev_info, prev_block_stats, prev_interface_stats =
985088
+		  Hashtbl.find last_info id in
985088
+		Some prev_info, prev_block_stats, prev_interface_stats
985088
+	      with Not_found -> None, [], [] in
985088
+
985088
+	    Some (name,
985088
+                  Active {
985088
+		      rd_domid = id; rd_dom = dom; rd_info = info;
985088
+		      rd_block_stats = block_stats;
985088
+		      rd_interface_stats = interface_stats;
985088
+		      rd_prev_info = prev_info;
985088
+		      rd_prev_block_stats = prev_block_stats;
985088
+		      rd_prev_interface_stats = prev_interface_stats;
985088
+		      rd_cpu_time = 0.; rd_percent_cpu = 0.;
985088
+                      rd_mem_bytes = 0L; rd_mem_percent = 0L;
985088
+		      rd_block_rd_reqs = None; rd_block_wr_reqs = None;
985088
+                      rd_block_rd_bytes = None; rd_block_wr_bytes = None;
985088
+                      rd_block_rd_info = None; rd_block_wr_info = None;
985088
+		      rd_net_rx_bytes = None; rd_net_tx_bytes = None;
985088
+		    })
985088
+	  with
985088
+	    Libvirt.Virterror _ -> None (* ignore transient error *)
985088
+      ) ids in
985088
+
985088
+    (* Inactive domains. *)
985088
+    let doms_inactive =
985088
+      try
985088
+	let n = C.num_of_defined_domains conn in
985088
+	let names =
985088
+	  if n > 0 then Array.to_list (C.list_defined_domains conn n)
985088
+	  else [] in
985088
+	List.map (fun name -> name, Inactive) names
985088
+      with
985088
+      (* Ignore transient errors, in particular errors from
985088
+       * num_of_defined_domains if it cannot contact xend.
985088
+       *)
985088
+      | Libvirt.Virterror _ -> [] in
985088
+
985088
+    doms @ doms_inactive in
985088
+
985088
+  (* Calculate the CPU time (ns) and %CPU used by each domain. *)
985088
+  let doms =
985088
+    List.map (
985088
+      function
985088
+      (* We have previous CPU info from which to calculate it? *)
985088
+      | name, Active ({ rd_prev_info = Some prev_info } as rd) ->
985088
+	 let cpu_time =
985088
+	   Int64.to_float (rd.rd_info.D.cpu_time -^ prev_info.D.cpu_time) in
985088
+	 let percent_cpu = 100. *. cpu_time /. total_cpu in
985088
+         let mem_usage = rd.rd_info.D.memory in
985088
+         let mem_percent =
985088
+           100L *^ rd.rd_info.D.memory /^ node_info.C.memory in
985088
+	 let rd = { rd with
985088
+		    rd_cpu_time = cpu_time;
985088
+		    rd_percent_cpu = percent_cpu;
985088
+		    rd_mem_bytes = mem_usage;
985088
+                    rd_mem_percent = mem_percent} in
985088
+	 name, Active rd
985088
+      (* For all other domains we can't calculate it, so leave as 0 *)
985088
+      | rd -> rd
985088
+    ) doms in
985088
+
985088
+  (* Calculate the number of block device read/write requests across
985088
+   * all block devices attached to a domain.
985088
+   *)
985088
+  let doms =
985088
+    List.map (
985088
+      function
985088
+      (* Do we have stats from the previous slice? *)
985088
+      | name, Active ({ rd_prev_block_stats = ((_::_) as prev_block_stats) }
985088
+		      as rd) ->
985088
+	 let block_stats = rd.rd_block_stats in (* stats now *)
985088
+
985088
+	 (* Add all the devices together.  Throw away device names. *)
985088
+	 let prev_block_stats =
985088
+	   sum_block_stats (List.map snd prev_block_stats) in
985088
+	 let block_stats =
985088
+	   sum_block_stats (List.map snd block_stats) in
985088
+
985088
+	 (* Calculate increase in read & write requests. *)
985088
+	 let read_reqs =
985088
+	   block_stats.D.rd_req -^ prev_block_stats.D.rd_req in
985088
+	 let write_reqs =
985088
+	   block_stats.D.wr_req -^ prev_block_stats.D.wr_req in
985088
+         let read_bytes =
985088
+           block_stats.D.rd_bytes -^ prev_block_stats.D.rd_bytes in
985088
+         let write_bytes =
985088
+           block_stats.D.wr_bytes -^ prev_block_stats.D.wr_bytes in
985088
+
985088
+	 let rd = { rd with
985088
+		    rd_block_rd_reqs = Some read_reqs;
985088
+		    rd_block_wr_reqs = Some write_reqs;
985088
+                    rd_block_rd_bytes = Some read_bytes;
985088
+                    rd_block_wr_bytes = Some write_bytes;
985088
+         } in
985088
+         let rd = { rd with
985088
+                    rd_block_rd_info =
985088
+                      if block_in_bytes then
985088
+                        rd.rd_block_rd_bytes else rd.rd_block_rd_reqs;
985088
+                    rd_block_wr_info =
985088
+                      if block_in_bytes then
985088
+                        rd.rd_block_wr_bytes else rd.rd_block_wr_reqs;
985088
+         } in
985088
+	 name, Active rd
985088
+      (* For all other domains we can't calculate it, so leave as None. *)
985088
+      | rd -> rd
985088
+    ) doms in
985088
+
985088
+  (* Calculate the same as above for network interfaces across
985088
+   * all network interfaces attached to a domain.
985088
+   *)
985088
+  let doms =
985088
+    List.map (
985088
+      function
985088
+      (* Do we have stats from the previous slice? *)
985088
+      | name, Active ({ rd_prev_interface_stats =
985088
+			  ((_::_) as prev_interface_stats) }
985088
+		      as rd) ->
985088
+	 let interface_stats = rd.rd_interface_stats in (* stats now *)
985088
+
985088
+	 (* Add all the devices together.  Throw away device names. *)
985088
+	 let prev_interface_stats =
985088
+	   sum_interface_stats (List.map snd prev_interface_stats) in
985088
+	 let interface_stats =
985088
+	   sum_interface_stats (List.map snd interface_stats) in
985088
+
985088
+	 (* Calculate increase in rx & tx bytes. *)
985088
+	 let rx_bytes =
985088
+	   interface_stats.D.rx_bytes -^ prev_interface_stats.D.rx_bytes in
985088
+	 let tx_bytes =
985088
+	   interface_stats.D.tx_bytes -^ prev_interface_stats.D.tx_bytes in
985088
+
985088
+	 let rd = { rd with
985088
+		    rd_net_rx_bytes = Some rx_bytes;
985088
+		    rd_net_tx_bytes = Some tx_bytes } in
985088
+	 name, Active rd
985088
+      (* For all other domains we can't calculate it, so leave as None. *)
985088
+      | rd -> rd
985088
+    ) doms in
985088
+
985088
+  (* Calculate totals. *)
985088
+  let totals =
985088
+    List.fold_left (
985088
+        fun (count, running, blocked, paused, shutdown, shutoff,
985088
+	     crashed, active, inactive,
985088
+	     total_cpu_time, total_memory, total_domU_memory) ->
985088
+	function
985088
+	| (name, Active rd) ->
985088
+	   let test state orig =
985088
+	     if rd.rd_info.D.state = state then orig+1 else orig
985088
+	   in
985088
+	   let running = test D.InfoRunning running in
985088
+	   let blocked = test D.InfoBlocked blocked in
985088
+	   let paused = test D.InfoPaused paused in
985088
+	   let shutdown = test D.InfoShutdown shutdown in
985088
+	   let shutoff = test D.InfoShutoff shutoff in
985088
+	   let crashed = test D.InfoCrashed crashed in
985088
+
985088
+	   let total_cpu_time = total_cpu_time +. rd.rd_cpu_time in
985088
+	   let total_memory = total_memory +^ rd.rd_info.D.memory in
985088
+	   let total_domU_memory =
985088
+             total_domU_memory +^
985088
+	       if rd.rd_domid > 0 then rd.rd_info.D.memory else 0L in
985088
+
985088
+	   (count+1, running, blocked, paused, shutdown, shutoff,
985088
+	    crashed, active+1, inactive,
985088
+	    total_cpu_time, total_memory, total_domU_memory)
985088
+
985088
+	| (name, Inactive) -> (* inactive domain *)
985088
+	   (count+1, running, blocked, paused, shutdown, shutoff,
985088
+	    crashed, active, inactive+1,
985088
+	    total_cpu_time, total_memory, total_domU_memory)
985088
+    ) (0,0,0,0,0,0,0,0,0, 0.,0L,0L) doms in
985088
+
985088
+  (* Update last_time, last_info. *)
985088
+  last_time := time;
985088
+  Hashtbl.clear last_info;
985088
+  List.iter (
985088
+    function
985088
+    | (_, Active rd) ->
985088
+       let info = rd.rd_info, rd.rd_block_stats, rd.rd_interface_stats in
985088
+       Hashtbl.add last_info rd.rd_domid info
985088
+    | _ -> ()
985088
+  ) doms;
985088
+
985088
+  { rd_doms = doms;
985088
+    rd_time = time;
985088
+    rd_printable_time = printable_time;
985088
+    rd_nr_pcpus = nr_pcpus;
985088
+    rd_total_cpu = total_cpu;
985088
+    rd_total_cpu_per_pcpu = total_cpu_per_pcpu;
985088
+    rd_totals = totals }
985088
+
985088
+(* Collect some extra information in PCPUDisplay display_mode. *)
985088
+let collect_pcpu { rd_doms = doms; rd_nr_pcpus = nr_pcpus } =
985088
+  (* Get the VCPU info and VCPU->PCPU mappings for active domains.
985088
+   * Also cull some data we don't care about.
985088
+   *)
985088
+  let doms =
985088
+    List.filter_map (
985088
+      function
985088
+      | (name, Active rd) ->
985088
+	 (try
985088
+	     let domid = rd.rd_domid in
985088
+	     let maplen = C.cpumaplen nr_pcpus in
985088
+	     let cpu_stats = D.get_cpu_stats rd.rd_dom in
985088
+
985088
+             (* Note the terminology is confusing.
985088
+              *
985088
+              * In libvirt, cpu_time is the total time (hypervisor +
985088
+              * vCPU).  vcpu_time is the time only taken by the vCPU,
985088
+              * excluding time taken inside the hypervisor.
985088
+              *
985088
+              * For each pCPU, libvirt may return either "cpu_time"
985088
+              * or "vcpu_time" or neither or both.  This function
985088
+              * returns an array pair [|cpu_time, vcpu_time|];
985088
+              * if either is missing it is returned as 0.
985088
+              *)
985088
+	     let find_cpu_usages params =
985088
+               let rec find_uint64_field name = function
985088
+                 | (n, D.TypedFieldUInt64 usage) :: _ when n = name ->
985088
+                    usage
985088
+                 | _ :: params -> find_uint64_field name params
985088
+                 | [] -> 0L
985088
+               in
985088
+               [| find_uint64_field "cpu_time" params;
985088
+                  find_uint64_field "vcpu_time" params |]
985088
+             in
985088
+
985088
+	     let pcpu_usages = Array.map find_cpu_usages cpu_stats in
985088
+	     let maxinfo = rd.rd_info.D.nr_virt_cpu in
985088
+	     let nr_vcpus, vcpu_infos, cpumaps =
985088
+	       D.get_vcpus rd.rd_dom maxinfo maplen in
985088
+
985088
+	     (* Got previous pcpu_usages for this domain? *)
985088
+	     let prev_pcpu_usages =
985088
+	       try Some (Hashtbl.find last_pcpu_usages domid)
985088
+	       with Not_found -> None in
985088
+	     (* Update last_pcpu_usages. *)
985088
+	     Hashtbl.replace last_pcpu_usages domid pcpu_usages;
985088
+
985088
+	     (match prev_pcpu_usages with
985088
+	      | Some prev_pcpu_usages
985088
+		   when Array.length prev_pcpu_usages = Array.length pcpu_usages ->
985088
+		 Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages,
985088
+		       prev_pcpu_usages, cpumaps, maplen)
985088
+	      | _ -> None (* ignore missing / unequal length prev_vcpu_infos *)
985088
+	     );
985088
+	   with
985088
+	     Libvirt.Virterror _ -> None (* ignore transient libvirt errors *)
985088
+	 )
985088
+      | (_, Inactive) -> None (* ignore inactive doms *)
985088
+    ) doms in
985088
+  let nr_doms = List.length doms in
985088
+
985088
+  (* Rearrange the data into a matrix.  Major axis (down) is
985088
+   * pCPUs.  Minor axis (right) is domains.  At each node we store:
985088
+   *  cpu_time hypervisor + domain (on this pCPU only, nanosecs),
985088
+   *  vcpu_time domain only (on this pCPU only, nanosecs).
985088
+   *)
985088
+  let make_3d_array dimx dimy dimz e =
985088
+    Array.init dimx (fun _ -> Array.make_matrix dimy dimz e)
985088
+  in
985088
+  let pcpus = make_3d_array nr_pcpus nr_doms 2 0L in
985088
+
985088
+  List.iteri (
985088
+    fun di (domid, name, nr_vcpus, vcpu_infos, pcpu_usages,
985088
+	    prev_pcpu_usages, cpumaps, maplen) ->
985088
+      (* Which pCPUs can this dom run on? *)
985088
+      for p = 0 to Array.length pcpu_usages - 1 do
985088
+	pcpus.(p).(di).(0) <-
985088
+          pcpu_usages.(p).(0) -^ prev_pcpu_usages.(p).(0);
985088
+	pcpus.(p).(di).(1) <-
985088
+          pcpu_usages.(p).(1) -^ prev_pcpu_usages.(p).(1)
985088
+      done
985088
+  ) doms;
985088
+
985088
+  (* Sum the total CPU time used by each pCPU, for the %CPU column. *)
985088
+  let pcpus_cpu_time =
985088
+    Array.map (
985088
+      fun row ->
985088
+        let cpu_time = ref 0L in
985088
+	for di = 0 to Array.length row-1 do
985088
+	  let t = row.(di).(0) in
985088
+	  cpu_time := !cpu_time +^ t
985088
+	done;
985088
+	Int64.to_float !cpu_time
985088
+    ) pcpus in
985088
+
985088
+  { rd_pcpu_doms = doms;
985088
+    rd_pcpu_pcpus = pcpus;
985088
+    rd_pcpu_pcpus_cpu_time = pcpus_cpu_time }
985088
diff --git a/src/collect.mli b/src/collect.mli
985088
new file mode 100644
985088
index 0000000..440859b
985088
--- /dev/null
985088
+++ b/src/collect.mli
985088
@@ -0,0 +1,86 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(* Hook for [Opt_xml] to override (if present). *)
985088
+val parse_device_xml :
985088
+  (int -> [ `R ] Libvirt.Domain.t -> string list * string list) ref
985088
+
985088
+(* Intermediate "domain + stats" structure that we use to collect
985088
+ * everything we know about a domain within the collect function.
985088
+ *)
985088
+type rd_domain = Inactive | Active of rd_active
985088
+and rd_active = {
985088
+  rd_domid : int;			(* Domain ID. *)
985088
+  rd_dom : [`R] Libvirt.Domain.t;       (* Domain object. *)
985088
+  rd_info : Libvirt.Domain.info;        (* Domain CPU info now. *)
985088
+  rd_block_stats : (string * Libvirt.Domain.block_stats) list;
985088
+                                        (* Domain block stats now. *)
985088
+  rd_interface_stats : (string * Libvirt.Domain.interface_stats) list;
985088
+                                        (* Domain net stats now. *)
985088
+  rd_prev_info : Libvirt.Domain.info option; (* Domain CPU info previously. *)
985088
+  rd_prev_block_stats : (string * Libvirt.Domain.block_stats) list;
985088
+                                        (* Domain block stats prev. *)
985088
+  rd_prev_interface_stats : (string * Libvirt.Domain.interface_stats) list;
985088
+                                        (* Domain interface stats prev. *)
985088
+  (* The following are since the last slice, or 0 if cannot be calculated: *)
985088
+  rd_cpu_time : float;			(* CPU time used in nanoseconds. *)
985088
+  rd_percent_cpu : float;		(* CPU time as percent of total. *)
985088
+  rd_mem_bytes : int64;		        (* Memory usage in bytes *)
985088
+  rd_mem_percent: int64;		(* Memory usage as percent of total *)
985088
+  (* The following are since the last slice, or None if cannot be calc'd: *)
985088
+  rd_block_rd_reqs : int64 option;      (* Number of block device read rqs. *)
985088
+  rd_block_wr_reqs : int64 option;      (* Number of block device write rqs. *)
985088
+  rd_block_rd_bytes : int64 option;     (* Number of bytes block device read *)
985088
+  rd_block_wr_bytes : int64 option;     (* Number of bytes block device write *)
985088
+  (* _info fields includes the number considering --block_in_bytes option *)
985088
+  rd_block_rd_info : int64 option;      (* Block device read info for user *)
985088
+  rd_block_wr_info : int64 option;      (* Block device read info for user *)
985088
+
985088
+  rd_net_rx_bytes : int64 option;	(* Number of bytes received. *)
985088
+  rd_net_tx_bytes : int64 option;	(* Number of bytes transmitted. *)
985088
+}
985088
+
985088
+type stats = {
985088
+  rd_doms : (string * rd_domain) list;  (* List of domains. *)
985088
+  rd_time : float;
985088
+  rd_printable_time : string;
985088
+  rd_nr_pcpus : int;
985088
+  rd_total_cpu : float;
985088
+  rd_total_cpu_per_pcpu : float;
985088
+  rd_totals : (int * int * int * int * int * int * int * int * int * float *
985088
+                 int64 * int64);
985088
+}
985088
+
985088
+type pcpu_stats = {
985088
+  rd_pcpu_doms : (int * string * int *
985088
+                  Libvirt.Domain.vcpu_info array * int64 array array *
985088
+                  int64 array array * string * int) list;
985088
+  rd_pcpu_pcpus : int64 array array array;
985088
+  rd_pcpu_pcpus_cpu_time : float array
985088
+}
985088
+
985088
+val collect : Types.setup -> bool -> stats
985088
+(** Collect statistics. *)
985088
+
985088
+val collect_pcpu : stats -> pcpu_stats
985088
+(** Used in PCPUDisplay mode only, this returns extra per-PCPU stats. *)
985088
+
985088
+val clear_pcpu_display_data : unit -> unit
985088
+(** Clear the cache of pcpu_usages used by PCPUDisplay display_mode
985088
+    when we switch back to TaskDisplay mode. *)
985088
diff --git a/src/csv_output.ml b/src/csv_output.ml
985088
new file mode 100644
985088
index 0000000..9496ca8
985088
--- /dev/null
985088
+++ b/src/csv_output.ml
985088
@@ -0,0 +1,118 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(* CSV output functions. *)
985088
+
985088
+open Printf
985088
+open ExtList
985088
+
985088
+open Collect
985088
+
985088
+module C = Libvirt.Connect
985088
+
985088
+(* Hook for CSV support (see [opt_csv.ml]). *)
985088
+let csv_write : (string list -> unit) ref =
985088
+  ref (
985088
+    fun _ -> ()
985088
+  )
985088
+
985088
+(* Write CSV header row. *)
985088
+let write_csv_header (csv_cpu, csv_mem, csv_block, csv_net) block_in_bytes =
985088
+  (!csv_write) (
985088
+    [ "Hostname"; "Time"; "Arch"; "Physical CPUs";
985088
+      "Count"; "Running"; "Blocked"; "Paused"; "Shutdown";
985088
+      "Shutoff"; "Crashed"; "Active"; "Inactive";
985088
+      "%CPU";
985088
+      "Total hardware memory (KB)";
985088
+      "Total memory (KB)"; "Total guest memory (KB)";
985088
+      "Total CPU time (ns)" ] @
985088
+      (* These fields are repeated for each domain: *)
985088
+    [ "Domain ID"; "Domain name"; ] @
985088
+    (if csv_cpu then [ "CPU (ns)"; "%CPU"; ] else []) @
985088
+    (if csv_mem then [ "Mem (bytes)"; "%Mem";] else []) @
985088
+    (if csv_block && not block_in_bytes
985088
+       then [ "Block RDRQ"; "Block WRRQ"; ] else []) @
985088
+    (if csv_block && block_in_bytes
985088
+       then [ "Block RDBY"; "Block WRBY"; ] else []) @
985088
+    (if csv_net then [ "Net RXBY"; "Net TXBY" ] else [])
985088
+  )
985088
+
985088
+(* Write summary data to CSV file. *)
985088
+let append_csv (_, _, _, _, _, node_info, hostname, _) (* setup *)
985088
+               (csv_cpu, csv_mem, csv_block, csv_net)
985088
+               { rd_doms = doms;
985088
+                 rd_printable_time = printable_time;
985088
+                 rd_nr_pcpus = nr_pcpus; rd_total_cpu = total_cpu;
985088
+                 rd_totals = totals } (* state *) =
985088
+  (* The totals / summary fields. *)
985088
+  let (count, running, blocked, paused, shutdown, shutoff,
985088
+       crashed, active, inactive,
985088
+       total_cpu_time, total_memory, total_domU_memory) = totals in
985088
+
985088
+  let percent_cpu = 100. *. total_cpu_time /. total_cpu in
985088
+
985088
+  let summary_fields = [
985088
+    hostname; printable_time; node_info.C.model; string_of_int nr_pcpus;
985088
+    string_of_int count; string_of_int running; string_of_int blocked;
985088
+    string_of_int paused; string_of_int shutdown; string_of_int shutoff;
985088
+    string_of_int crashed; string_of_int active; string_of_int inactive;
985088
+    sprintf "%2.1f" percent_cpu;
985088
+    Int64.to_string node_info.C.memory;
985088
+    Int64.to_string total_memory; Int64.to_string total_domU_memory;
985088
+    Int64.to_string (Int64.of_float total_cpu_time)
985088
+  ] in
985088
+
985088
+  (* The domains.
985088
+   *
985088
+   * Sort them by ID so that the list of relatively stable.  Ignore
985088
+   * inactive domains.
985088
+   *)
985088
+  let doms = List.filter_map (
985088
+    function
985088
+    | _, Inactive -> None		(* Ignore inactive domains. *)
985088
+    | name, Active rd -> Some (name, rd)
985088
+  ) doms in
985088
+  let cmp (_, { rd_domid = rd_domid1 }) (_, { rd_domid = rd_domid2 }) =
985088
+    compare rd_domid1 rd_domid2
985088
+  in
985088
+  let doms = List.sort ~cmp doms in
985088
+
985088
+  let string_of_int64_option = Option.map_default Int64.to_string "" in
985088
+
985088
+  let domain_fields = List.map (
985088
+    fun (domname, rd) ->
985088
+      [ string_of_int rd.rd_domid; domname ] @
985088
+	(if csv_cpu then [
985088
+	   string_of_float rd.rd_cpu_time; string_of_float rd.rd_percent_cpu
985088
+	 ] else []) @
985088
+        (if csv_mem then [
985088
+            Int64.to_string rd.rd_mem_bytes; Int64.to_string rd.rd_mem_percent
985088
+         ] else []) @
985088
+	(if csv_block then [
985088
+	   string_of_int64_option rd.rd_block_rd_info;
985088
+	   string_of_int64_option rd.rd_block_wr_info;
985088
+	 ] else []) @
985088
+	(if csv_net then [
985088
+	   string_of_int64_option rd.rd_net_rx_bytes;
985088
+	   string_of_int64_option rd.rd_net_tx_bytes;
985088
+	 ] else [])
985088
+  ) doms in
985088
+  let domain_fields = List.flatten domain_fields in
985088
+
985088
+  (!csv_write) (summary_fields @ domain_fields)
985088
diff --git a/src/csv_output.mli b/src/csv_output.mli
985088
new file mode 100644
985088
index 0000000..d5eab0f
985088
--- /dev/null
985088
+++ b/src/csv_output.mli
985088
@@ -0,0 +1,27 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(** CSV output functions. *)
985088
+
985088
+(* Hook for [Opt_csv] to override (if present). *)
985088
+val csv_write : (string list -> unit) ref
985088
+
985088
+val write_csv_header : bool * bool * bool * bool -> bool -> unit
985088
+
985088
+val append_csv : Types.setup -> bool * bool * bool * bool -> Collect.stats -> unit
985088
diff --git a/src/opt_csv.ml b/src/opt_csv.ml
985088
index 6c3b2be..6625c61 100644
985088
--- a/src/opt_csv.ml
985088
+++ b/src/opt_csv.ml
985088
@@ -28,7 +28,7 @@ Top.csv_start :=
985088
   fun filename ->
985088
     chan := Some (open_out filename) ;;
985088
 
985088
-Top.csv_write :=
985088
+Csv_output.csv_write :=
985088
   fun row ->
985088
     match !chan with
985088
     | None -> ()			(* CSV output not enabled. *)
985088
diff --git a/src/opt_xml.ml b/src/opt_xml.ml
985088
index bb83780..1037b85 100644
985088
--- a/src/opt_xml.ml
985088
+++ b/src/opt_xml.ml
985088
@@ -27,7 +27,7 @@ module C = Libvirt.Connect
985088
 module D = Libvirt.Domain
985088
 module N = Libvirt.Network ;;
985088
 
985088
-Top.parse_device_xml :=
985088
+Collect.parse_device_xml :=
985088
 fun id dom ->
985088
   try
985088
     let xml = D.get_xml_desc dom in
985088
diff --git a/src/redraw.ml b/src/redraw.ml
985088
new file mode 100644
985088
index 0000000..9ce889b
985088
--- /dev/null
985088
+++ b/src/redraw.ml
985088
@@ -0,0 +1,506 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+open ExtList
985088
+open Curses
985088
+open Printf
985088
+
985088
+open Opt_gettext.Gettext
985088
+open Utils
985088
+open Types
985088
+open Screen
985088
+open Collect
985088
+
985088
+module C = Libvirt.Connect
985088
+module D = Libvirt.Domain
985088
+
985088
+(* Keep a historical list of %CPU usages. *)
985088
+let historical_cpu = ref []
985088
+let historical_cpu_last_time = ref (Unix.gettimeofday ())
985088
+
985088
+(* Redraw the display. *)
985088
+let redraw display_mode sort_order
985088
+           (_, _, _, _, _, node_info, _, _) (* setup *)
985088
+           block_in_bytes
985088
+           historical_cpu_delay
985088
+           { rd_doms = doms;
985088
+             rd_time = time; rd_printable_time = printable_time;
985088
+             rd_nr_pcpus = nr_pcpus;
985088
+             rd_total_cpu = total_cpu;
985088
+             rd_total_cpu_per_pcpu = total_cpu_per_pcpu;
985088
+             rd_totals = totals } (* state *)
985088
+           pcpu_display =
985088
+  clear ();
985088
+
985088
+  (* Get the screen/window size. *)
985088
+  let lines, cols = get_size () in
985088
+
985088
+  (* Time. *)
985088
+  mvaddstr top_lineno 0 (sprintf "virt-top %s - " printable_time);
985088
+
985088
+  (* Basic node_info. *)
985088
+  addstr
985088
+    (sprintf "%s %d/%dCPU %dMHz %LdMB "
985088
+	     node_info.C.model node_info.C.cpus nr_pcpus node_info.C.mhz
985088
+	     (node_info.C.memory /^ 1024L));
985088
+  (* Save the cursor position for when we come to draw the
985088
+   * historical CPU times (down in this function).
985088
+   *)
985088
+  let stdscr = stdscr () in
985088
+  let historical_cursor = getyx stdscr in
985088
+
985088
+  (match display_mode with
985088
+
985088
+   (*---------- Showing domains ----------*)
985088
+   | TaskDisplay ->
985088
+      (* Sort domains on current sort_order. *)
985088
+      let doms =
985088
+	let cmp =
985088
+	  match sort_order with
985088
+	  | DomainName ->
985088
+	     (fun _ -> 0) (* fallthrough to default name compare *)
985088
+	  | Processor ->
985088
+	     (function
985088
+	       | Active rd1, Active rd2 ->
985088
+		  compare rd2.rd_percent_cpu rd1.rd_percent_cpu
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | Memory ->
985088
+	     (function
985088
+	       | Active { rd_info = info1 }, Active { rd_info = info2 } ->
985088
+		  compare info2.D.memory info1.D.memory
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | Time ->
985088
+	     (function
985088
+	       | Active { rd_info = info1 }, Active { rd_info = info2 } ->
985088
+		  compare info2.D.cpu_time info1.D.cpu_time
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | DomainID ->
985088
+	     (function
985088
+	       | Active { rd_domid = id1 }, Active { rd_domid = id2 } ->
985088
+		  compare id1 id2
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | NetRX ->
985088
+	     (function
985088
+	       | Active { rd_net_rx_bytes = r1 }, Active { rd_net_rx_bytes = r2 } ->
985088
+		  compare r2 r1
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | NetTX ->
985088
+	     (function
985088
+	       | Active { rd_net_tx_bytes = r1 }, Active { rd_net_tx_bytes = r2 } ->
985088
+		  compare r2 r1
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | BlockRdRq ->
985088
+	     (function
985088
+	       | Active { rd_block_rd_reqs = r1 }, Active { rd_block_rd_reqs = r2 } ->
985088
+		  compare r2 r1
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	  | BlockWrRq ->
985088
+	     (function
985088
+	       | Active { rd_block_wr_reqs = r1 }, Active { rd_block_wr_reqs = r2 } ->
985088
+		  compare r2 r1
985088
+	       | Active _, Inactive -> -1
985088
+	       | Inactive, Active _ -> 1
985088
+	       | Inactive, Inactive -> 0)
985088
+	in
985088
+	let cmp (name1, dom1) (name2, dom2) =
985088
+	  let r = cmp (dom1, dom2) in
985088
+	  if r <> 0 then r
985088
+	  else compare name1 name2
985088
+	in
985088
+	List.sort ~cmp doms in
985088
+
985088
+      (* Print domains. *)
985088
+      attron A.reverse;
985088
+      let header_string =
985088
+        if block_in_bytes
985088
+        then "   ID S RDBY WRBY RXBY TXBY %CPU %MEM    TIME   NAME"
985088
+        else "   ID S RDRQ WRRQ RXBY TXBY %CPU %MEM    TIME   NAME"
985088
+      in
985088
+      mvaddstr header_lineno 0
985088
+	       (pad cols header_string);
985088
+      attroff A.reverse;
985088
+
985088
+      let rec loop lineno = function
985088
+	| [] -> ()
985088
+	| (name, Active rd) :: doms ->
985088
+	   if lineno < lines then (
985088
+	     let state = show_state rd.rd_info.D.state in
985088
+	     let rd_req = Show.int64_option rd.rd_block_rd_info in
985088
+	     let wr_req = Show.int64_option rd.rd_block_wr_info in
985088
+	     let rx_bytes = Show.int64_option rd.rd_net_rx_bytes in
985088
+	     let tx_bytes = Show.int64_option rd.rd_net_tx_bytes in
985088
+	     let percent_cpu = Show.percent rd.rd_percent_cpu in
985088
+	     let percent_mem = Int64.to_float rd.rd_mem_percent in
985088
+	     let percent_mem = Show.percent percent_mem in
985088
+	     let time = Show.time rd.rd_info.D.cpu_time in
985088
+
985088
+	     let line =
985088
+               sprintf "%5d %c %s %s %s %s %s %s %s %s"
985088
+		       rd.rd_domid state rd_req wr_req rx_bytes tx_bytes
985088
+		       percent_cpu percent_mem time name in
985088
+	     let line = pad cols line in
985088
+	     mvaddstr lineno 0 line;
985088
+	     loop (lineno+1) doms
985088
+	   )
985088
+	| (name, Inactive) :: doms -> (* inactive domain *)
985088
+	   if lineno < lines then (
985088
+	     let line =
985088
+	       sprintf
985088
+		 "    -                                           (%s)"
985088
+		 name in
985088
+	     let line = pad cols line in
985088
+	     mvaddstr lineno 0 line;
985088
+	     loop (lineno+1) doms
985088
+	   )
985088
+      in
985088
+      loop domains_lineno doms
985088
+
985088
+   (*---------- Showing physical CPUs ----------*)
985088
+   | PCPUDisplay ->
985088
+      let { rd_pcpu_doms = doms;
985088
+            rd_pcpu_pcpus = pcpus;
985088
+            rd_pcpu_pcpus_cpu_time = pcpus_cpu_time } =
985088
+	match pcpu_display with
985088
+	| Some p -> p
985088
+	| None -> failwith "internal error: no pcpu_display data" in
985088
+
985088
+      (* Display the pCPUs. *)
985088
+      let dom_names =
985088
+	String.concat "" (
985088
+	                List.map (
985088
+	                    fun (_, name, _, _, _, _, _, _) ->
985088
+		            let len = String.length name in
985088
+		            let width = max (len+1) 12 in
985088
+		            pad width name
985088
+	                  ) doms
985088
+	              ) in
985088
+      attron A.reverse;
985088
+      mvaddstr header_lineno 0 (pad cols ("PHYCPU %CPU " ^ dom_names));
985088
+      attroff A.reverse;
985088
+
985088
+      Array.iteri (
985088
+	fun p row ->
985088
+	  mvaddstr (p+domains_lineno) 0 (sprintf "%4d   " p);
985088
+	  let cpu_time = pcpus_cpu_time.(p) in (* ns used on this CPU *)
985088
+	  let percent_cpu = 100. *. cpu_time /. total_cpu_per_pcpu in
985088
+	  addstr (Show.percent percent_cpu);
985088
+	  addch ' ';
985088
+
985088
+	  List.iteri (
985088
+	    fun di (domid, name, _, _, _, _, _, _) ->
985088
+	      let t = pcpus.(p).(di).(0) in (* hypervisor + domain *)
985088
+	      let t_only = pcpus.(p).(di).(1) in (* domain only *)
985088
+	      let len = String.length name in
985088
+	      let width = max (len+1) 12 in
985088
+	      let str_t =
985088
+		if t <= 0L then ""
985088
+		else (
985088
+		  let t = Int64.to_float t in
985088
+		  let percent = 100. *. t /. total_cpu_per_pcpu in
985088
+		  Show.percent percent
985088
+		) in
985088
+              let str_t_only =
985088
+                if t_only <= 0L then ""
985088
+                else (
985088
+                  let t_only = Int64.to_float t_only in
985088
+                  let percent = 100. *. t_only /. total_cpu_per_pcpu in
985088
+                  Show.percent percent
985088
+                ) in
985088
+              addstr (pad 5 str_t);
985088
+              addstr (pad 5 str_t_only);
985088
+              addstr (pad (width-10) " ");
985088
+	      ()
985088
+          ) doms
985088
+      ) pcpus;
985088
+
985088
+   (*---------- Showing network interfaces ----------*)
985088
+   | NetDisplay ->
985088
+      (* Only care about active domains. *)
985088
+      let doms =
985088
+        List.filter_map (
985088
+	    function
985088
+	    | (name, Active rd) -> Some (name, rd)
985088
+	    | (_, Inactive) -> None
985088
+	) doms in
985088
+
985088
+      (* For each domain we have a list of network interfaces seen
985088
+       * this slice, and seen in the previous slice, which we now
985088
+       * match up to get a list of (domain, interface) for which
985088
+       * we have current & previous knowledge.  (And ignore the rest).
985088
+       *)
985088
+      let devs =
985088
+	List.map (
985088
+	  fun (name, rd) ->
985088
+	    List.filter_map (
985088
+	      fun (dev, stats) ->
985088
+	        try
985088
+		  (* Have prev slice stats for this device? *)
985088
+		  let prev_stats =
985088
+		    List.assoc dev rd.rd_prev_interface_stats in
985088
+		  Some (dev, name, rd, stats, prev_stats)
985088
+		with Not_found -> None
985088
+	      ) rd.rd_interface_stats
985088
+	  ) doms in
985088
+
985088
+      (* Finally we have a list of:
985088
+       * device name, domain name, rd_* stuff, curr stats, prev stats.
985088
+       *)
985088
+      let devs : (string * string * rd_active *
985088
+		    D.interface_stats * D.interface_stats) list =
985088
+	List.flatten devs in
985088
+
985088
+      (* Difference curr slice & prev slice. *)
985088
+      let devs =
985088
+        List.map (
985088
+	  fun (dev, name, rd, curr, prev) ->
985088
+	    dev, name, rd, diff_interface_stats curr prev
985088
+	  ) devs in
985088
+
985088
+      (* Sort by current sort order, but map some of the standard
985088
+       * sort orders into ones which makes sense here.
985088
+       *)
985088
+      let devs =
985088
+	let cmp =
985088
+	  match sort_order with
985088
+	  | DomainName ->
985088
+	     (fun _ -> 0) (* fallthrough to default name compare *)
985088
+	  | DomainID ->
985088
+	     (fun (_, { rd_domid = id1 }, _, { rd_domid = id2 }) ->
985088
+	      compare id1 id2)
985088
+	  | Processor | Memory | Time
985088
+          | BlockRdRq | BlockWrRq
985088
+	     (* fallthrough to RXBY comparison. *)
985088
+	  | NetRX ->
985088
+	     (fun ({ D.rx_bytes = b1 }, _, { D.rx_bytes = b2 }, _) ->
985088
+	      compare b2 b1)
985088
+	  | NetTX ->
985088
+	     (fun ({ D.tx_bytes = b1 }, _, { D.tx_bytes = b2 }, _) ->
985088
+	      compare b2 b1)
985088
+	in
985088
+	let cmp (dev1, name1, rd1, stats1) (dev2, name2, rd2, stats2) =
985088
+	  let r = cmp (stats1, rd1, stats2, rd2) in
985088
+	  if r <> 0 then r
985088
+	  else compare (dev1, name1) (dev2, name2)
985088
+	in
985088
+	List.sort ~cmp devs in
985088
+
985088
+      (* Print the header for network devices. *)
985088
+      attron A.reverse;
985088
+      mvaddstr header_lineno 0
985088
+	       (pad cols "   ID S RXBY TXBY RXPK TXPK DOMAIN       INTERFACE");
985088
+      attroff A.reverse;
985088
+
985088
+      (* Print domains and devices. *)
985088
+      let rec loop lineno = function
985088
+	| [] -> ()
985088
+	| (dev, name, rd, stats) :: devs ->
985088
+	   if lineno < lines then (
985088
+	     let state = show_state rd.rd_info.D.state in
985088
+	     let rx_bytes =
985088
+	       if stats.D.rx_bytes >= 0L
985088
+	       then Show.int64 stats.D.rx_bytes
985088
+	       else "    " in
985088
+	     let tx_bytes =
985088
+	       if stats.D.tx_bytes >= 0L
985088
+	       then Show.int64 stats.D.tx_bytes
985088
+	       else "    " in
985088
+	     let rx_packets =
985088
+	       if stats.D.rx_packets >= 0L
985088
+	       then Show.int64 stats.D.rx_packets
985088
+	       else "    " in
985088
+	     let tx_packets =
985088
+	       if stats.D.tx_packets >= 0L
985088
+	       then Show.int64 stats.D.tx_packets
985088
+	       else "    " in
985088
+
985088
+	     let line = sprintf "%5d %c %s %s %s %s %-12s %s"
985088
+		                rd.rd_domid state
985088
+		                rx_bytes tx_bytes
985088
+		                rx_packets tx_packets
985088
+		                (pad 12 name) dev in
985088
+	     let line = pad cols line in
985088
+	     mvaddstr lineno 0 line;
985088
+	     loop (lineno+1) devs
985088
+	   )
985088
+      in
985088
+      loop domains_lineno devs
985088
+
985088
+   (*---------- Showing block devices ----------*)
985088
+   | BlockDisplay ->
985088
+      (* Only care about active domains. *)
985088
+      let doms =
985088
+        List.filter_map (
985088
+	    function
985088
+	    | (name, Active rd) -> Some (name, rd)
985088
+	    | (_, Inactive) -> None
985088
+	) doms in
985088
+
985088
+      (* For each domain we have a list of block devices seen
985088
+       * this slice, and seen in the previous slice, which we now
985088
+       * match up to get a list of (domain, device) for which
985088
+       * we have current & previous knowledge.  (And ignore the rest).
985088
+       *)
985088
+      let devs =
985088
+	List.map (
985088
+	  fun (name, rd) ->
985088
+	    List.filter_map (
985088
+	      fun (dev, stats) ->
985088
+	        try
985088
+		  (* Have prev slice stats for this device? *)
985088
+		  let prev_stats =
985088
+		    List.assoc dev rd.rd_prev_block_stats in
985088
+		  Some (dev, name, rd, stats, prev_stats)
985088
+		with Not_found -> None
985088
+	    ) rd.rd_block_stats
985088
+	) doms in
985088
+
985088
+      (* Finally we have a list of:
985088
+       * device name, domain name, rd_* stuff, curr stats, prev stats.
985088
+       *)
985088
+      let devs : (string * string * rd_active *
985088
+		    D.block_stats * D.block_stats) list =
985088
+	List.flatten devs in
985088
+
985088
+      (* Difference curr slice & prev slice. *)
985088
+      let devs =
985088
+        List.map (
985088
+	  fun (dev, name, rd, curr, prev) ->
985088
+	    dev, name, rd, diff_block_stats curr prev
985088
+        ) devs in
985088
+
985088
+      (* Sort by current sort order, but map some of the standard
985088
+       * sort orders into ones which makes sense here.
985088
+       *)
985088
+      let devs =
985088
+	let cmp =
985088
+	  match sort_order with
985088
+	  | DomainName ->
985088
+	     (fun _ -> 0) (* fallthrough to default name compare *)
985088
+	  | DomainID ->
985088
+	     (fun (_, { rd_domid = id1 }, _, { rd_domid = id2 }) ->
985088
+	      compare id1 id2)
985088
+	  | Processor | Memory | Time
985088
+          | NetRX | NetTX
985088
+	     (* fallthrough to RDRQ comparison. *)
985088
+	  | BlockRdRq ->
985088
+	     (fun ({ D.rd_req = b1 }, _, { D.rd_req = b2 }, _) ->
985088
+	      compare b2 b1)
985088
+	  | BlockWrRq ->
985088
+	     (fun ({ D.wr_req = b1 }, _, { D.wr_req = b2 }, _) ->
985088
+	      compare b2 b1)
985088
+	in
985088
+	let cmp (dev1, name1, rd1, stats1) (dev2, name2, rd2, stats2) =
985088
+	  let r = cmp (stats1, rd1, stats2, rd2) in
985088
+	  if r <> 0 then r
985088
+	  else compare (dev1, name1) (dev2, name2)
985088
+	in
985088
+	List.sort ~cmp devs in
985088
+
985088
+      (* Print the header for block devices. *)
985088
+      attron A.reverse;
985088
+      mvaddstr header_lineno 0
985088
+	       (pad cols "   ID S RDBY WRBY RDRQ WRRQ DOMAIN       DEVICE");
985088
+      attroff A.reverse;
985088
+
985088
+      (* Print domains and devices. *)
985088
+      let rec loop lineno = function
985088
+	| [] -> ()
985088
+	| (dev, name, rd, stats) :: devs ->
985088
+	   if lineno < lines then (
985088
+	     let state = show_state rd.rd_info.D.state in
985088
+	     let rd_bytes =
985088
+	       if stats.D.rd_bytes >= 0L
985088
+	       then Show.int64 stats.D.rd_bytes
985088
+	       else "    " in
985088
+	     let wr_bytes =
985088
+	       if stats.D.wr_bytes >= 0L
985088
+	       then Show.int64 stats.D.wr_bytes
985088
+	       else "    " in
985088
+	     let rd_req =
985088
+	       if stats.D.rd_req >= 0L
985088
+	       then Show.int64 stats.D.rd_req
985088
+	       else "    " in
985088
+	     let wr_req =
985088
+	       if stats.D.wr_req >= 0L
985088
+	       then Show.int64 stats.D.wr_req
985088
+	       else "    " in
985088
+
985088
+	     let line = sprintf "%5d %c %s %s %s %s %-12s %s"
985088
+		                rd.rd_domid state
985088
+		                rd_bytes wr_bytes
985088
+		                rd_req wr_req
985088
+		                (pad 12 name) dev in
985088
+	     let line = pad cols line in
985088
+	     mvaddstr lineno 0 line;
985088
+	     loop (lineno+1) devs
985088
+	   )
985088
+      in
985088
+      loop domains_lineno devs
985088
+  ); (* end of display_mode conditional section *)
985088
+
985088
+  let (count, running, blocked, paused, shutdown, shutoff,
985088
+       crashed, active, inactive,
985088
+       total_cpu_time, total_memory, total_domU_memory) = totals in
985088
+
985088
+  mvaddstr summary_lineno 0
985088
+           (sprintf
985088
+	      (f_"%d domains, %d active, %d running, %d sleeping, %d paused, %d inactive D:%d O:%d X:%d")
985088
+	      count active running blocked paused inactive shutdown shutoff crashed);
985088
+
985088
+  (* Total %CPU used, and memory summary. *)
985088
+  let percent_cpu = 100. *. total_cpu_time /. total_cpu in
985088
+  mvaddstr (summary_lineno+1) 0
985088
+           (sprintf
985088
+	      (f_"CPU: %2.1f%%  Mem: %Ld MB (%Ld MB by guests)")
985088
+	      percent_cpu (total_memory /^ 1024L) (total_domU_memory /^ 1024L));
985088
+
985088
+  (* Time to grab another historical %CPU for the list? *)
985088
+  if time >= !historical_cpu_last_time +. float historical_cpu_delay
985088
+  then (
985088
+    historical_cpu := percent_cpu :: List.take 10 !historical_cpu;
985088
+    historical_cpu_last_time := time
985088
+  );
985088
+
985088
+  (* Display historical CPU time. *)
985088
+  let () =
985088
+    let y, x = historical_cursor in
985088
+    let maxwidth = cols - x in
985088
+    let line =
985088
+      String.concat " "
985088
+	            (List.map (sprintf "%2.1f%%") !historical_cpu) in
985088
+    let line = pad maxwidth line in
985088
+    mvaddstr y x line;
985088
+    () in
985088
+
985088
+  move message_lineno 0; (* Park cursor in message area, as with top. *)
985088
+  refresh ()             (* Refresh the display. *)
985088
diff --git a/src/redraw.mli b/src/redraw.mli
985088
new file mode 100644
985088
index 0000000..2ea97c3
985088
--- /dev/null
985088
+++ b/src/redraw.mli
985088
@@ -0,0 +1,20 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+val redraw : Types.display -> Types.sort_order -> Types.setup -> bool -> int -> Collect.stats -> Collect.pcpu_stats option -> unit
985088
diff --git a/src/screen.ml b/src/screen.ml
985088
new file mode 100644
985088
index 0000000..0d847a2
985088
--- /dev/null
985088
+++ b/src/screen.ml
985088
@@ -0,0 +1,52 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(* The virt-top screen layout. *)
985088
+
985088
+open Curses
985088
+
985088
+module D = Libvirt.Domain
985088
+
985088
+(* Line numbers. *)
985088
+let top_lineno = 0
985088
+let summary_lineno = 1 (* this takes 2 lines *)
985088
+let message_lineno = 3
985088
+let header_lineno = 4
985088
+let domains_lineno = 5
985088
+
985088
+(* Easier to use versions of curses functions addstr, mvaddstr, etc. *)
985088
+let move y x = ignore (move y x)
985088
+let refresh () = ignore (refresh ())
985088
+let addch c = ignore (addch (int_of_char c))
985088
+let addstr s = ignore (addstr s)
985088
+let mvaddstr y x s = ignore (mvaddstr y x s)
985088
+
985088
+(* Print in the "message area". *)
985088
+let clear_msg () = move message_lineno 0; clrtoeol ()
985088
+let print_msg str = clear_msg (); mvaddstr message_lineno 0 str
985088
+
985088
+(* Show a libvirt domain state (the 'S' column). *)
985088
+let show_state = function
985088
+  | D.InfoNoState -> '?'
985088
+  | D.InfoRunning -> 'R'
985088
+  | D.InfoBlocked -> 'S'
985088
+  | D.InfoPaused -> 'P'
985088
+  | D.InfoShutdown -> 'D'
985088
+  | D.InfoShutoff -> 'O'
985088
+  | D.InfoCrashed -> 'X'
985088
diff --git a/src/screen.mli b/src/screen.mli
985088
new file mode 100644
985088
index 0000000..a8a23a0
985088
--- /dev/null
985088
+++ b/src/screen.mli
985088
@@ -0,0 +1,41 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(** The virt-top screen layout. *)
985088
+
985088
+(* Line numbers. *)
985088
+val top_lineno : int
985088
+val summary_lineno : int (** this takes 2 lines *)
985088
+val message_lineno : int
985088
+val header_lineno : int
985088
+val domains_lineno : int
985088
+
985088
+(* Easier to use versions of curses functions addstr, mvaddstr, etc. *)
985088
+val move : int -> int -> unit
985088
+val refresh : unit -> unit
985088
+val addch : char -> unit
985088
+val addstr : string -> unit
985088
+val mvaddstr : int -> int -> string -> unit
985088
+
985088
+(* Print in the "message area". *)
985088
+val clear_msg : unit -> unit
985088
+val print_msg : string -> unit
985088
+
985088
+(* Show a libvirt domain state (the 'S' column). *)
985088
+val show_state : Libvirt.Domain.state -> char
985088
diff --git a/src/stream_output.ml b/src/stream_output.ml
985088
new file mode 100644
985088
index 0000000..bf7b114
985088
--- /dev/null
985088
+++ b/src/stream_output.ml
985088
@@ -0,0 +1,84 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(* [--stream] mode output functions. *)
985088
+
985088
+open Printf
985088
+open ExtList
985088
+
985088
+open Utils
985088
+open Collect
985088
+
985088
+module C = Libvirt.Connect
985088
+module D = Libvirt.Domain
985088
+
985088
+let append_stream (_, _, _, _, _, node_info, hostname, _) (* setup *)
985088
+                  block_in_bytes
985088
+                  { rd_doms = doms;
985088
+                    rd_printable_time = printable_time;
985088
+                    rd_nr_pcpus = nr_pcpus; rd_total_cpu = total_cpu;
985088
+                    rd_totals = totals } (* state *) =
985088
+  (* Header for this iteration *)
985088
+  printf "virt-top time  %s Host %s %s %d/%dCPU %dMHz %LdMB \n"
985088
+    printable_time hostname node_info.C.model node_info.C.cpus nr_pcpus
985088
+    node_info.C.mhz (node_info.C.memory /^ 1024L);
985088
+  (* dump domain information one by one *)
985088
+   let rd, wr = if block_in_bytes then "RDBY", "WRBY" else "RDRQ", "WRRQ"
985088
+   in
985088
+     printf "   ID S %s %s RXBY TXBY %%CPU %%MEM   TIME    NAME\n" rd wr;
985088
+
985088
+  (* sort by ID *)
985088
+  let doms =
985088
+    let compare =
985088
+      (function
985088
+       | Active {rd_domid = id1 }, Active {rd_domid = id2} ->
985088
+           compare id1 id2
985088
+       | Active _, Inactive -> -1
985088
+       | Inactive, Active _ -> 1
985088
+       | Inactive, Inactive -> 0)
985088
+    in
985088
+    let cmp  (name1, dom1) (name2, dom2) = compare(dom1, dom2) in
985088
+    List.sort ~cmp doms in
985088
+  (*Print domains *)
985088
+  let dump_domain = fun name rd
985088
+  -> begin
985088
+    let state = Screen.show_state rd.rd_info.D.state in
985088
+         let rd_req = if rd.rd_block_rd_info = None then "   0"
985088
+                      else Show.int64_option rd.rd_block_rd_info in
985088
+         let wr_req = if rd.rd_block_wr_info = None then "   0"
985088
+                      else Show.int64_option rd.rd_block_wr_info in
985088
+    let rx_bytes = if rd.rd_net_rx_bytes = None then "   0"
985088
+    else Show.int64_option rd.rd_net_rx_bytes in
985088
+    let tx_bytes = if rd.rd_net_tx_bytes = None then "   0"
985088
+    else Show.int64_option rd.rd_net_tx_bytes in
985088
+    let percent_cpu = Show.percent rd.rd_percent_cpu in
985088
+    let percent_mem = Int64.to_float rd.rd_mem_percent in
985088
+    let percent_mem = Show.percent percent_mem in
985088
+    let time = Show.time rd.rd_info.D.cpu_time in
985088
+    printf "%5d %c %s %s %s %s %s %s %s %s\n"
985088
+      rd.rd_domid state rd_req wr_req rx_bytes tx_bytes
985088
+      percent_cpu percent_mem time name;
985088
+  end
985088
+  in
985088
+  List.iter (
985088
+    function
985088
+    | name, Active dom -> dump_domain name dom
985088
+    | name, Inactive -> ()
985088
+  ) doms;
985088
+  flush stdout
985088
diff --git a/src/stream_output.mli b/src/stream_output.mli
985088
new file mode 100644
985088
index 0000000..c45e548
985088
--- /dev/null
985088
+++ b/src/stream_output.mli
985088
@@ -0,0 +1,22 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(** [--stream] mode output functions. *)
985088
+
985088
+val append_stream : Types.setup -> bool -> Collect.stats -> unit
985088
diff --git a/src/top.ml b/src/top.ml
985088
index f50e6a8..204f3b6 100644
985088
--- a/src/top.ml
985088
+++ b/src/top.ml
985088
@@ -1,5 +1,5 @@
985088
 (* 'top'-like tool for libvirt domains.
985088
-   (C) Copyright 2007-2009 Richard W.M. Jones, Red Hat Inc.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
    http://libvirt.org/
985088
 
985088
    This program is free software; you can redistribute it and/or modify
985088
@@ -23,6 +23,9 @@ open Curses
985088
 
985088
 open Opt_gettext.Gettext
985088
 open Utils
985088
+open Types
985088
+open Collect
985088
+open Screen
985088
 
985088
 module C = Libvirt.Connect
985088
 module D = Libvirt.Domain
985088
@@ -30,21 +33,11 @@ module N = Libvirt.Network
985088
 
985088
 let rcfile = ".virt-toprc"
985088
 
985088
-(* Hook for XML support (see [opt_xml.ml]). *)
985088
-let parse_device_xml : (int -> [>`R] D.t -> string list * string list) ref =
985088
-  ref (
985088
-    fun _ _ -> [], []
985088
-  )
985088
-
985088
 (* Hooks for CSV support (see [opt_csv.ml]). *)
985088
 let csv_start : (string -> unit) ref =
985088
   ref (
985088
     fun _ -> failwith (s_"virt-top was compiled without support for CSV files")
985088
   )
985088
-let csv_write : (string list -> unit) ref =
985088
-  ref (
985088
-    fun _ -> ()
985088
-  )
985088
 
985088
 (* Hook for calendar support (see [opt_calendar.ml]). *)
985088
 let parse_date_time : (string -> float) ref =
985088
@@ -53,62 +46,6 @@ let parse_date_time : (string -> float) ref =
985088
       failwith (s_"virt-top was compiled without support for dates and times")
985088
   )
985088
 
985088
-(* Sort order. *)
985088
-type sort_order =
985088
-  | DomainID | DomainName | Processor | Memory | Time
985088
-  | NetRX | NetTX | BlockRdRq | BlockWrRq
985088
-let all_sort_fields = [
985088
-  DomainID; DomainName; Processor; Memory; Time;
985088
-  NetRX; NetTX; BlockRdRq; BlockWrRq
985088
-]
985088
-let printable_sort_order = function
985088
-  | Processor -> s_"%CPU"
985088
-  | Memory -> s_"%MEM"
985088
-  | Time -> s_"TIME (CPU time)"
985088
-  | DomainID -> s_"Domain ID"
985088
-  | DomainName -> s_"Domain name"
985088
-  | NetRX -> s_"Net RX bytes"
985088
-  | NetTX -> s_"Net TX bytes"
985088
-  | BlockRdRq -> s_"Block read reqs"
985088
-  | BlockWrRq -> s_"Block write reqs"
985088
-let sort_order_of_cli = function
985088
-  | "cpu" | "processor" -> Processor
985088
-  | "mem" | "memory" -> Memory
985088
-  | "time" -> Time
985088
-  | "id" -> DomainID
985088
-  | "name" -> DomainName
985088
-  | "netrx" -> NetRX | "nettx" -> NetTX
985088
-  | "blockrdrq" -> BlockRdRq | "blockwrrq" -> BlockWrRq
985088
-  | str ->
985088
-      failwithf (f_"%s: sort order should be: %s")
985088
-	str "cpu|mem|time|id|name|netrx|nettx|blockrdrq|blockwrrq"
985088
-let cli_of_sort_order = function
985088
-  | Processor -> "cpu"
985088
-  | Memory -> "mem"
985088
-  | Time -> "time"
985088
-  | DomainID -> "id"
985088
-  | DomainName -> "name"
985088
-  | NetRX -> "netrx"
985088
-  | NetTX -> "nettx"
985088
-  | BlockRdRq -> "blockrdrq"
985088
-  | BlockWrRq -> "blockwrrq"
985088
-
985088
-(* Current major display mode: TaskDisplay is the normal display. *)
985088
-type display = TaskDisplay | PCPUDisplay | BlockDisplay | NetDisplay
985088
-
985088
-let display_of_cli = function
985088
-  | "task" -> TaskDisplay
985088
-  | "pcpu" -> PCPUDisplay
985088
-  | "block" -> BlockDisplay
985088
-  | "net" -> NetDisplay
985088
-  | str ->
985088
-      failwithf (f_"%s: display should be %s") str "task|pcpu|block|net"
985088
-let cli_of_display = function
985088
-  | TaskDisplay -> "task"
985088
-  | PCPUDisplay -> "pcpu"
985088
-  | BlockDisplay -> "block"
985088
-  | NetDisplay -> "net"
985088
-
985088
 (* Init file. *)
985088
 type init_file = NoInitFile | DefaultInitFile | InitFile of string
985088
 
985088
@@ -134,11 +71,6 @@ let script_mode = ref false
985088
 let stream_mode = ref false
985088
 let block_in_bytes = ref false
985088
 
985088
-(* Tuple of never-changing data returned by start_up function. *)
985088
-type setup =
985088
-    Libvirt.ro C.t * bool * bool * bool * bool * C.node_info * string *
985088
-      (int * int * int)
985088
-
985088
 (* Function to read command line arguments and go into curses mode. *)
985088
 let start_up () =
985088
   (* Read command line arguments. *)
985088
@@ -352,16 +284,6 @@ OPTIONS" in
985088
    node_info, hostname, libvirt_version (* info that doesn't change *)
985088
   )
985088
 
985088
-(* Show a domain state (the 'S' column). *)
985088
-let show_state = function
985088
-  | D.InfoNoState -> '?'
985088
-  | D.InfoRunning -> 'R'
985088
-  | D.InfoBlocked -> 'S'
985088
-  | D.InfoPaused -> 'P'
985088
-  | D.InfoShutdown -> 'D'
985088
-  | D.InfoShutoff -> 'O'
985088
-  | D.InfoCrashed -> 'X'
985088
-
985088
 (* Sleep in seconds. *)
985088
 let sleep = Unix.sleep
985088
 
985088
@@ -387,1039 +309,33 @@ let get_string maxlen =
985088
       Not_found -> str (* it is full maxlen bytes *)
985088
   )
985088
 
985088
-(* Line numbers. *)
985088
-let top_lineno = 0
985088
-let summary_lineno = 1 (* this takes 2 lines *)
985088
-let message_lineno = 3
985088
-let header_lineno = 4
985088
-let domains_lineno = 5
985088
-
985088
-(* Easier to use versions of curses functions addstr, mvaddstr, etc. *)
985088
-let move y x = ignore (move y x)
985088
-let refresh () = ignore (refresh ())
985088
-let addch c = ignore (addch (int_of_char c))
985088
-let addstr s = ignore (addstr s)
985088
-let mvaddstr y x s = ignore (mvaddstr y x s)
985088
-
985088
-(* Print in the "message area". *)
985088
-let clear_msg () = move message_lineno 0; clrtoeol ()
985088
-let print_msg str = clear_msg (); mvaddstr message_lineno 0 str
985088
-
985088
-(* Intermediate "domain + stats" structure that we use to collect
985088
- * everything we know about a domain within the collect function.
985088
- *)
985088
-type rd_domain = Inactive | Active of rd_active
985088
-and rd_active = {
985088
-  rd_domid : int;			(* Domain ID. *)
985088
-  rd_dom : [`R] D.t;			(* Domain object. *)
985088
-  rd_info : D.info;			(* Domain CPU info now. *)
985088
-  rd_block_stats : (string * D.block_stats) list;
985088
-                                        (* Domain block stats now. *)
985088
-  rd_interface_stats : (string * D.interface_stats) list;
985088
-                                        (* Domain net stats now. *)
985088
-  rd_prev_info : D.info option;		(* Domain CPU info previously. *)
985088
-  rd_prev_block_stats : (string * D.block_stats) list;
985088
-                                        (* Domain block stats prev. *)
985088
-  rd_prev_interface_stats : (string * D.interface_stats) list;
985088
-                                        (* Domain interface stats prev. *)
985088
-  (* The following are since the last slice, or 0 if cannot be calculated: *)
985088
-  rd_cpu_time : float;			(* CPU time used in nanoseconds. *)
985088
-  rd_percent_cpu : float;		(* CPU time as percent of total. *)
985088
-  rd_mem_bytes : int64;		        (* Memory usage in bytes *)
985088
-  rd_mem_percent: int64;		(* Memory usage as percent of total *)
985088
-  (* The following are since the last slice, or None if cannot be calc'd: *)
985088
-  rd_block_rd_reqs : int64 option;      (* Number of block device read rqs. *)
985088
-  rd_block_wr_reqs : int64 option;      (* Number of block device write rqs. *)
985088
-  rd_block_rd_bytes : int64 option;   (* Number of bytes block device read *)
985088
-  rd_block_wr_bytes : int64 option;   (* Number of bytes block device write *)
985088
-  (* _info fields includes the number considering --block_in_bytes option *)
985088
-  rd_block_rd_info : int64 option;    (* Block device read info for user *)
985088
-  rd_block_wr_info : int64 option;    (* Block device read info for user *)
985088
-
985088
-  rd_net_rx_bytes : int64 option;	(* Number of bytes received. *)
985088
-  rd_net_tx_bytes : int64 option;	(* Number of bytes transmitted. *)
985088
-}
985088
-
985088
-(* Collect stats. *)
985088
-let collect, clear_pcpu_display_data =
985088
-  (* We cache the list of block devices and interfaces for each domain
985088
-   * here, so we don't need to reparse the XML each time.
985088
-   *)
985088
-  let devices = Hashtbl.create 13 in
985088
-
985088
-  (* Function to get the list of block devices, network interfaces for
985088
-   * a particular domain.  Get it from the devices cache, and if not
985088
-   * there then parse the domain XML.
985088
-   *)
985088
-  let get_devices id dom =
985088
-    try Hashtbl.find devices id
985088
-    with Not_found ->
985088
-      let blkdevs, netifs = (!parse_device_xml) id dom in
985088
-      Hashtbl.replace devices id (blkdevs, netifs);
985088
-      blkdevs, netifs
985088
-  in
985088
-
985088
-  (* We save the state of domains across redraws here, which allows us
985088
-   * to deduce %CPU usage from the running total.
985088
-   *)
985088
-  let last_info = Hashtbl.create 13 in
985088
-  let last_time = ref (Unix.gettimeofday ()) in
985088
-
985088
-  (* Save pcpu_usages structures across redraws too (only for pCPU display). *)
985088
-  let last_pcpu_usages = Hashtbl.create 13 in
985088
-
985088
-  let clear_pcpu_display_data () =
985088
-    (* Clear out pcpu_usages used by PCPUDisplay display_mode
985088
-     * when we switch back to TaskDisplay mode.
985088
-     *)
985088
-    Hashtbl.clear last_pcpu_usages
985088
-  in
985088
-
985088
-  let collect (conn, _, _, _, _, node_info, _, _) =
985088
-    (* Number of physical CPUs (some may be disabled). *)
985088
-    let nr_pcpus = C.maxcpus_of_node_info node_info in
985088
-
985088
-    (* Get the current time. *)
985088
-    let time = Unix.gettimeofday () in
985088
-    let tm = Unix.localtime time in
985088
-    let printable_time =
985088
-      sprintf "%02d:%02d:%02d" tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec in
985088
-
985088
-    (* What's the total CPU time elapsed since we were last called? (ns) *)
985088
-    let total_cpu_per_pcpu = 1_000_000_000. *. (time -. !last_time) in
985088
-    (* Avoid division by zero. *)
985088
-    let total_cpu_per_pcpu =
985088
-      if total_cpu_per_pcpu <= 0. then 1. else total_cpu_per_pcpu in
985088
-    let total_cpu = float node_info.C.cpus *. total_cpu_per_pcpu in
985088
-
985088
-    (* Get the domains.  Match up with their last_info (if any). *)
985088
-    let doms =
985088
-      (* Active domains. *)
985088
-      let n = C.num_of_domains conn in
985088
-      let ids =
985088
-	if n > 0 then Array.to_list (C.list_domains conn n)
985088
-	else [] in
985088
-      let doms =
985088
-	List.filter_map (
985088
-	  fun id ->
985088
-	    try
985088
-	      let dom = D.lookup_by_id conn id in
985088
-	      let name = D.get_name dom in
985088
-	      let blkdevs, netifs = get_devices id dom in
985088
-
985088
-	      (* Get current CPU, block and network stats. *)
985088
-	      let info = D.get_info dom in
985088
-	      let block_stats =
985088
-		try List.map (fun dev -> dev, D.block_stats dom dev) blkdevs
985088
-		with
985088
-		| Libvirt.Not_supported "virDomainBlockStats"
985088
-		| Libvirt.Virterror _ -> [] in
985088
-	      let interface_stats =
985088
-		try List.map (fun dev -> dev, D.interface_stats dom dev) netifs
985088
-		with
985088
-		| Libvirt.Not_supported "virDomainInterfaceStats"
985088
-		| Libvirt.Virterror _ -> [] in
985088
-
985088
-	      let prev_info, prev_block_stats, prev_interface_stats =
985088
-		try
985088
-		  let prev_info, prev_block_stats, prev_interface_stats =
985088
-		    Hashtbl.find last_info id in
985088
-		  Some prev_info, prev_block_stats, prev_interface_stats
985088
-		with Not_found -> None, [], [] in
985088
-
985088
-	      Some (name, Active {
985088
-		      rd_domid = id; rd_dom = dom; rd_info = info;
985088
-		      rd_block_stats = block_stats;
985088
-		      rd_interface_stats = interface_stats;
985088
-		      rd_prev_info = prev_info;
985088
-		      rd_prev_block_stats = prev_block_stats;
985088
-		      rd_prev_interface_stats = prev_interface_stats;
985088
-		      rd_cpu_time = 0.; rd_percent_cpu = 0.;
985088
-                      rd_mem_bytes = 0L; rd_mem_percent = 0L;
985088
-		      rd_block_rd_reqs = None; rd_block_wr_reqs = None;
985088
-                      rd_block_rd_bytes = None; rd_block_wr_bytes = None;
985088
-                      rd_block_rd_info = None; rd_block_wr_info = None;
985088
-		      rd_net_rx_bytes = None; rd_net_tx_bytes = None;
985088
-		    })
985088
-	    with
985088
-	      Libvirt.Virterror _ -> None (* ignore transient error *)
985088
-	) ids in
985088
-
985088
-      (* Inactive domains. *)
985088
-      let doms_inactive =
985088
-	try
985088
-	  let n = C.num_of_defined_domains conn in
985088
-	  let names =
985088
-	    if n > 0 then Array.to_list (C.list_defined_domains conn n)
985088
-	    else [] in
985088
-	  List.map (fun name -> name, Inactive) names
985088
-	with
985088
-	  (* Ignore transient errors, in particular errors from
985088
-	   * num_of_defined_domains if it cannot contact xend.
985088
-	   *)
985088
-	| Libvirt.Virterror _ -> [] in
985088
-
985088
-      doms @ doms_inactive in
985088
-
985088
-    (* Calculate the CPU time (ns) and %CPU used by each domain. *)
985088
-    let doms =
985088
-      List.map (
985088
-	function
985088
-	(* We have previous CPU info from which to calculate it? *)
985088
-	| name, Active ({ rd_prev_info = Some prev_info } as rd) ->
985088
-	    let cpu_time =
985088
-	      Int64.to_float (rd.rd_info.D.cpu_time -^ prev_info.D.cpu_time) in
985088
-	    let percent_cpu = 100. *. cpu_time /. total_cpu in
985088
-            let mem_usage = rd.rd_info.D.memory in
985088
-            let mem_percent =
985088
-                100L *^ rd.rd_info.D.memory /^ node_info.C.memory in
985088
-	    let rd = { rd with
985088
-			 rd_cpu_time = cpu_time;
985088
-			 rd_percent_cpu = percent_cpu;
985088
-			 rd_mem_bytes = mem_usage;
985088
-                         rd_mem_percent = mem_percent} in
985088
-	    name, Active rd
985088
-	(* For all other domains we can't calculate it, so leave as 0 *)
985088
-	| rd -> rd
985088
-      ) doms in
985088
-
985088
-    (* Calculate the number of block device read/write requests across
985088
-     * all block devices attached to a domain.
985088
-     *)
985088
-    let doms =
985088
-      List.map (
985088
-	function
985088
-	(* Do we have stats from the previous slice? *)
985088
-	| name, Active ({ rd_prev_block_stats = ((_::_) as prev_block_stats) }
985088
-			  as rd) ->
985088
-	    let block_stats = rd.rd_block_stats in (* stats now *)
985088
-
985088
-	    (* Add all the devices together.  Throw away device names. *)
985088
-	    let prev_block_stats =
985088
-	      sum_block_stats (List.map snd prev_block_stats) in
985088
-	    let block_stats =
985088
-	      sum_block_stats (List.map snd block_stats) in
985088
-
985088
-	    (* Calculate increase in read & write requests. *)
985088
-	    let read_reqs =
985088
-	      block_stats.D.rd_req -^ prev_block_stats.D.rd_req in
985088
-	    let write_reqs =
985088
-	      block_stats.D.wr_req -^ prev_block_stats.D.wr_req in
985088
-            let read_bytes =
985088
-              block_stats.D.rd_bytes -^ prev_block_stats.D.rd_bytes in
985088
-            let write_bytes =
985088
-              block_stats.D.wr_bytes -^ prev_block_stats.D.wr_bytes in
985088
-
985088
-	    let rd = { rd with
985088
-			 rd_block_rd_reqs = Some read_reqs;
985088
-			 rd_block_wr_reqs = Some write_reqs;
985088
-                         rd_block_rd_bytes = Some read_bytes;
985088
-                         rd_block_wr_bytes = Some write_bytes;
985088
-            } in
985088
-            let rd = { rd with
985088
-                         rd_block_rd_info = if !block_in_bytes then
985088
-                         rd.rd_block_rd_bytes else rd.rd_block_rd_reqs;
985088
-                         rd_block_wr_info = if !block_in_bytes then
985088
-                         rd.rd_block_wr_bytes else rd.rd_block_wr_reqs;
985088
-            } in
985088
-	    name, Active rd
985088
-	(* For all other domains we can't calculate it, so leave as None. *)
985088
-	| rd -> rd
985088
-      ) doms in
985088
-
985088
-    (* Calculate the same as above for network interfaces across
985088
-     * all network interfaces attached to a domain.
985088
-     *)
985088
-    let doms =
985088
-      List.map (
985088
-	function
985088
-	(* Do we have stats from the previous slice? *)
985088
-	| name, Active ({ rd_prev_interface_stats =
985088
-			      ((_::_) as prev_interface_stats) }
985088
-			  as rd) ->
985088
-	    let interface_stats = rd.rd_interface_stats in (* stats now *)
985088
-
985088
-	    (* Add all the devices together.  Throw away device names. *)
985088
-	    let prev_interface_stats =
985088
-	      sum_interface_stats (List.map snd prev_interface_stats) in
985088
-	    let interface_stats =
985088
-	      sum_interface_stats (List.map snd interface_stats) in
985088
-
985088
-	    (* Calculate increase in rx & tx bytes. *)
985088
-	    let rx_bytes =
985088
-	      interface_stats.D.rx_bytes -^ prev_interface_stats.D.rx_bytes in
985088
-	    let tx_bytes =
985088
-	      interface_stats.D.tx_bytes -^ prev_interface_stats.D.tx_bytes in
985088
-
985088
-	    let rd = { rd with
985088
-			 rd_net_rx_bytes = Some rx_bytes;
985088
-			 rd_net_tx_bytes = Some tx_bytes } in
985088
-	    name, Active rd
985088
-	(* For all other domains we can't calculate it, so leave as None. *)
985088
-	| rd -> rd
985088
-      ) doms in
985088
-
985088
-    (* Collect some extra information in PCPUDisplay display_mode. *)
985088
-    let pcpu_display =
985088
-      if !display_mode = PCPUDisplay then (
985088
-	(* Get the VCPU info and VCPU->PCPU mappings for active domains.
985088
-	 * Also cull some data we don't care about.
985088
-	 *)
985088
-	let doms = List.filter_map (
985088
-	  function
985088
-	  | (name, Active rd) ->
985088
-	      (try
985088
-		 let domid = rd.rd_domid in
985088
-		 let maplen = C.cpumaplen nr_pcpus in
985088
-		 let cpu_stats = D.get_cpu_stats rd.rd_dom in
985088
-
985088
-                 (* Note the terminology is confusing.
985088
-                  *
985088
-                  * In libvirt, cpu_time is the total time (hypervisor + vCPU).
985088
-                  * vcpu_time is the time only taken by the vCPU,
985088
-                  * excluding time taken inside the hypervisor.
985088
-                  *
985088
-                  * For each pCPU, libvirt may return either "cpu_time"
985088
-                  * or "vcpu_time" or neither or both.  This function
985088
-                  * returns an array pair [|cpu_time, vcpu_time|];
985088
-                  * if either is missing it is returned as 0.
985088
-                  *)
985088
-		 let find_cpu_usages params =
985088
-                   let rec find_uint64_field name = function
985088
-                     | (n, D.TypedFieldUInt64 usage) :: _ when n = name -> usage
985088
-                     | _ :: params -> find_uint64_field name params
985088
-                     | [] -> 0L
985088
-                   in
985088
-                   [| find_uint64_field "cpu_time" params;
985088
-                      find_uint64_field "vcpu_time" params |]
985088
-                 in
985088
-
985088
-		 let pcpu_usages = Array.map find_cpu_usages cpu_stats in
985088
-		 let maxinfo = rd.rd_info.D.nr_virt_cpu in
985088
-		 let nr_vcpus, vcpu_infos, cpumaps =
985088
-		   D.get_vcpus rd.rd_dom maxinfo maplen in
985088
-
985088
-		 (* Got previous pcpu_usages for this domain? *)
985088
-		 let prev_pcpu_usages =
985088
-		   try Some (Hashtbl.find last_pcpu_usages domid)
985088
-		   with Not_found -> None in
985088
-		 (* Update last_pcpu_usages. *)
985088
-		 Hashtbl.replace last_pcpu_usages domid pcpu_usages;
985088
-
985088
-		 (match prev_pcpu_usages with
985088
-		  | Some prev_pcpu_usages
985088
-		      when Array.length prev_pcpu_usages = Array.length pcpu_usages ->
985088
-		      Some (domid, name, nr_vcpus, vcpu_infos, pcpu_usages,
985088
-			    prev_pcpu_usages, cpumaps, maplen)
985088
-		  | _ -> None (* ignore missing / unequal length prev_vcpu_infos *)
985088
-		 );
985088
-	       with
985088
-		 Libvirt.Virterror _ -> None(* ignore transient libvirt errs *)
985088
-	      )
985088
-	  | (_, Inactive) -> None (* ignore inactive doms *)
985088
-	) doms in
985088
-	let nr_doms = List.length doms in
985088
-
985088
-	(* Rearrange the data into a matrix.  Major axis (down) is
985088
-	 * pCPUs.  Minor axis (right) is domains.  At each node we store:
985088
-	 *  cpu_time hypervisor + domain (on this pCPU only, nanosecs),
985088
-	 *  vcpu_time domain only (on this pCPU only, nanosecs).
985088
-	 *)
985088
-        let make_3d_array dimx dimy dimz e =
985088
-          Array.init dimx (fun _ -> Array.make_matrix dimy dimz e)
985088
-        in
985088
-	let pcpus = make_3d_array nr_pcpus nr_doms 2 0L in
985088
-
985088
-	List.iteri (
985088
-	  fun di (domid, name, nr_vcpus, vcpu_infos, pcpu_usages,
985088
-		  prev_pcpu_usages, cpumaps, maplen) ->
985088
-	    (* Which pCPUs can this dom run on? *)
985088
-	    for p = 0 to Array.length pcpu_usages - 1 do
985088
-	      pcpus.(p).(di).(0) <-
985088
-                pcpu_usages.(p).(0) -^ prev_pcpu_usages.(p).(0);
985088
-	      pcpus.(p).(di).(1) <-
985088
-                pcpu_usages.(p).(1) -^ prev_pcpu_usages.(p).(1)
985088
-            done
985088
-	) doms;
985088
-
985088
-	(* Sum the total CPU time used by each pCPU, for the %CPU column. *)
985088
-	let pcpus_cpu_time = Array.map (
985088
-	  fun row ->
985088
-	    let cpu_time = ref 0L in
985088
-	    for di = 0 to Array.length row-1 do
985088
-	      let t = row.(di).(0) in
985088
-	      cpu_time := !cpu_time +^ t
985088
-	    done;
985088
-	    Int64.to_float !cpu_time
985088
-	) pcpus in
985088
-
985088
-	Some (doms, pcpus, pcpus_cpu_time)
985088
-      ) else
985088
-	None in
985088
-
985088
-    (* Calculate totals. *)
985088
-    let totals = List.fold_left (
985088
-      fun (count, running, blocked, paused, shutdown, shutoff,
985088
-	   crashed, active, inactive,
985088
-	   total_cpu_time, total_memory, total_domU_memory) ->
985088
-	function
985088
-	| (name, Active rd) ->
985088
-	    let test state orig =
985088
-	      if rd.rd_info.D.state = state then orig+1 else orig
985088
-	    in
985088
-	    let running = test D.InfoRunning running in
985088
-	    let blocked = test D.InfoBlocked blocked in
985088
-	    let paused = test D.InfoPaused paused in
985088
-	    let shutdown = test D.InfoShutdown shutdown in
985088
-	    let shutoff = test D.InfoShutoff shutoff in
985088
-	    let crashed = test D.InfoCrashed crashed in
985088
-
985088
-	    let total_cpu_time = total_cpu_time +. rd.rd_cpu_time in
985088
-	    let total_memory = total_memory +^ rd.rd_info.D.memory in
985088
-	    let total_domU_memory = total_domU_memory +^
985088
-	      if rd.rd_domid > 0 then rd.rd_info.D.memory else 0L in
985088
-
985088
-	    (count+1, running, blocked, paused, shutdown, shutoff,
985088
-	     crashed, active+1, inactive,
985088
-	     total_cpu_time, total_memory, total_domU_memory)
985088
-
985088
-	| (name, Inactive) -> (* inactive domain *)
985088
-	    (count+1, running, blocked, paused, shutdown, shutoff,
985088
-	     crashed, active, inactive+1,
985088
-	     total_cpu_time, total_memory, total_domU_memory)
985088
-    ) (0,0,0,0,0,0,0,0,0, 0.,0L,0L) doms in
985088
-
985088
-    (* Update last_time, last_info. *)
985088
-    last_time := time;
985088
-    Hashtbl.clear last_info;
985088
-    List.iter (
985088
-      function
985088
-      | (_, Active rd) ->
985088
-	  let info = rd.rd_info, rd.rd_block_stats, rd.rd_interface_stats in
985088
-	  Hashtbl.add last_info rd.rd_domid info
985088
-      | _ -> ()
985088
-    ) doms;
985088
-
985088
-    (doms,
985088
-     time, printable_time,
985088
-     nr_pcpus, total_cpu, total_cpu_per_pcpu,
985088
-     totals,
985088
-     pcpu_display)
985088
-  in
985088
-
985088
-  collect, clear_pcpu_display_data
985088
-
985088
-(* Redraw the display. *)
985088
-let redraw =
985088
-  (* Keep a historical list of %CPU usages. *)
985088
-  let historical_cpu = ref [] in
985088
-  let historical_cpu_last_time = ref (Unix.gettimeofday ()) in
985088
-  fun
985088
-  (_, _, _, _, _, node_info, _, _) (* setup *)
985088
-  (doms,
985088
-   time, printable_time,
985088
-   nr_pcpus, total_cpu, total_cpu_per_pcpu,
985088
-   totals,
985088
-   pcpu_display) (* state *) ->
985088
-    clear ();
985088
-
985088
-    (* Get the screen/window size. *)
985088
-    let lines, cols = get_size () in
985088
-
985088
-    (* Time. *)
985088
-    mvaddstr top_lineno 0 (sprintf "virt-top %s - " printable_time);
985088
-
985088
-    (* Basic node_info. *)
985088
-    addstr
985088
-      (sprintf "%s %d/%dCPU %dMHz %LdMB "
985088
-	 node_info.C.model node_info.C.cpus nr_pcpus node_info.C.mhz
985088
-	 (node_info.C.memory /^ 1024L));
985088
-    (* Save the cursor position for when we come to draw the
985088
-     * historical CPU times (down in this function).
985088
-     *)
985088
-    let stdscr = stdscr () in
985088
-    let historical_cursor = getyx stdscr in
985088
-
985088
-    (match !display_mode with
985088
-     | TaskDisplay -> (*---------- Showing domains ----------*)
985088
-	 (* Sort domains on current sort_order. *)
985088
-	 let doms =
985088
-	   let cmp =
985088
-	     match !sort_order with
985088
-	     | DomainName ->
985088
-		 (fun _ -> 0) (* fallthrough to default name compare *)
985088
-	     | Processor ->
985088
-		 (function
985088
-		  | Active rd1, Active rd2 ->
985088
-		      compare rd2.rd_percent_cpu rd1.rd_percent_cpu
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | Memory ->
985088
-		 (function
985088
-		  | Active { rd_info = info1 }, Active { rd_info = info2 } ->
985088
-		      compare info2.D.memory info1.D.memory
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | Time ->
985088
-		 (function
985088
-		  | Active { rd_info = info1 }, Active { rd_info = info2 } ->
985088
-		      compare info2.D.cpu_time info1.D.cpu_time
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | DomainID ->
985088
-		 (function
985088
-		  | Active { rd_domid = id1 }, Active { rd_domid = id2 } ->
985088
-		      compare id1 id2
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | NetRX ->
985088
-		 (function
985088
-		  | Active { rd_net_rx_bytes = r1 }, Active { rd_net_rx_bytes = r2 } ->
985088
-		      compare r2 r1
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | NetTX ->
985088
-		 (function
985088
-		  | Active { rd_net_tx_bytes = r1 }, Active { rd_net_tx_bytes = r2 } ->
985088
-		      compare r2 r1
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | BlockRdRq ->
985088
-		 (function
985088
-		  | Active { rd_block_rd_reqs = r1 }, Active { rd_block_rd_reqs = r2 } ->
985088
-		      compare r2 r1
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	     | BlockWrRq ->
985088
-		 (function
985088
-		  | Active { rd_block_wr_reqs = r1 }, Active { rd_block_wr_reqs = r2 } ->
985088
-		      compare r2 r1
985088
-		  | Active _, Inactive -> -1
985088
-		  | Inactive, Active _ -> 1
985088
-		  | Inactive, Inactive -> 0)
985088
-	   in
985088
-	   let cmp (name1, dom1) (name2, dom2) =
985088
-	     let r = cmp (dom1, dom2) in
985088
-	     if r <> 0 then r
985088
-	     else compare name1 name2
985088
-	   in
985088
-	   List.sort ~cmp doms in
985088
-
985088
-	 (* Print domains. *)
985088
-	 attron A.reverse;
985088
-         let header_string = if !block_in_bytes
985088
-         then "   ID S RDBY WRBY RXBY TXBY %CPU %MEM    TIME   NAME"
985088
-         else "   ID S RDRQ WRRQ RXBY TXBY %CPU %MEM    TIME   NAME"
985088
-         in
985088
-	   mvaddstr header_lineno 0
985088
-	    (pad cols header_string);
985088
-	 attroff A.reverse;
985088
-
985088
-	 let rec loop lineno = function
985088
-	   | [] -> ()
985088
-	   | (name, Active rd) :: doms ->
985088
-	       if lineno < lines then (
985088
-		 let state = show_state rd.rd_info.D.state in
985088
-		 let rd_req = Show.int64_option rd.rd_block_rd_info in
985088
-		 let wr_req = Show.int64_option rd.rd_block_wr_info in
985088
-		 let rx_bytes = Show.int64_option rd.rd_net_rx_bytes in
985088
-		 let tx_bytes = Show.int64_option rd.rd_net_tx_bytes in
985088
-		 let percent_cpu = Show.percent rd.rd_percent_cpu in
985088
-		 let percent_mem = Int64.to_float rd.rd_mem_percent in
985088
-		 let percent_mem = Show.percent percent_mem in
985088
-		 let time = Show.time rd.rd_info.D.cpu_time in
985088
-
985088
-		 let line = sprintf "%5d %c %s %s %s %s %s %s %s %s"
985088
-		   rd.rd_domid state rd_req wr_req rx_bytes tx_bytes
985088
-		   percent_cpu percent_mem time name in
985088
-		 let line = pad cols line in
985088
-		 mvaddstr lineno 0 line;
985088
-		 loop (lineno+1) doms
985088
-	       )
985088
-	   | (name, Inactive) :: doms -> (* inactive domain *)
985088
-	       if lineno < lines then (
985088
-		 let line =
985088
-		   sprintf
985088
-		     "    -                                           (%s)"
985088
-		     name in
985088
-		 let line = pad cols line in
985088
-		 mvaddstr lineno 0 line;
985088
-		 loop (lineno+1) doms
985088
-	       )
985088
-	 in
985088
-	 loop domains_lineno doms
985088
-
985088
-     | PCPUDisplay -> (*---------- Showing physical CPUs ----------*)
985088
-	 let doms, pcpus, pcpus_cpu_time =
985088
-	   match pcpu_display with
985088
-	   | Some p -> p
985088
-	   | None -> failwith "internal error: no pcpu_display data" in
985088
-
985088
-	 (* Display the pCPUs. *)
985088
-	 let dom_names =
985088
-	   String.concat "" (
985088
-	     List.map (
985088
-	       fun (_, name, _, _, _, _, _, _) ->
985088
-		 let len = String.length name in
985088
-		 let width = max (len+1) 12 in
985088
-		 pad width name
985088
-	     ) doms
985088
-	   ) in
985088
-	 attron A.reverse;
985088
-	 mvaddstr header_lineno 0 (pad cols ("PHYCPU %CPU " ^ dom_names));
985088
-	 attroff A.reverse;
985088
-
985088
-	 Array.iteri (
985088
-	   fun p row ->
985088
-	     mvaddstr (p+domains_lineno) 0 (sprintf "%4d   " p);
985088
-	     let cpu_time = pcpus_cpu_time.(p) in (* ns used on this CPU *)
985088
-	     let percent_cpu = 100. *. cpu_time /. total_cpu_per_pcpu in
985088
-	     addstr (Show.percent percent_cpu);
985088
-	     addch ' ';
985088
-
985088
-	     List.iteri (
985088
-	       fun di (domid, name, _, _, _, _, _, _) ->
985088
-		 let t = pcpus.(p).(di).(0) in (* hypervisor + domain *)
985088
-		 let t_only = pcpus.(p).(di).(1) in (* domain only *)
985088
-		 let len = String.length name in
985088
-		 let width = max (len+1) 12 in
985088
-		 let str_t =
985088
-		   if t <= 0L then ""
985088
-		   else (
985088
-		     let t = Int64.to_float t in
985088
-		     let percent = 100. *. t /. total_cpu_per_pcpu in
985088
-		     Show.percent percent
985088
-		   ) in
985088
-                 let str_t_only =
985088
-                    if t_only <= 0L then ""
985088
-                    else (
985088
-                      let t_only = Int64.to_float t_only in
985088
-                      let percent = 100. *. t_only /. total_cpu_per_pcpu in
985088
-                      Show.percent percent
985088
-                    ) in
985088
-                 addstr (pad 5 str_t);
985088
-                 addstr (pad 5 str_t_only);
985088
-                 addstr (pad (width-10) " ");
985088
-		 ()
985088
-	     ) doms
985088
-	 ) pcpus;
985088
-
985088
-     | NetDisplay -> (*---------- Showing network interfaces ----------*)
985088
-	 (* Only care about active domains. *)
985088
-	 let doms = List.filter_map (
985088
-	   function
985088
-	   | (name, Active rd) -> Some (name, rd)
985088
-	   | (_, Inactive) -> None
985088
-	 ) doms in
985088
-
985088
-	 (* For each domain we have a list of network interfaces seen
985088
-	  * this slice, and seen in the previous slice, which we now
985088
-	  * match up to get a list of (domain, interface) for which
985088
-	  * we have current & previous knowledge.  (And ignore the rest).
985088
-	  *)
985088
-	 let devs =
985088
-	   List.map (
985088
-	     fun (name, rd) ->
985088
-	       List.filter_map (
985088
-		 fun (dev, stats) ->
985088
-		   try
985088
-		     (* Have prev slice stats for this device? *)
985088
-		     let prev_stats =
985088
-		       List.assoc dev rd.rd_prev_interface_stats in
985088
-		     Some (dev, name, rd, stats, prev_stats)
985088
-		   with Not_found -> None
985088
-	       ) rd.rd_interface_stats
985088
-	   ) doms in
985088
-
985088
-	 (* Finally we have a list of:
985088
-	  * device name, domain name, rd_* stuff, curr stats, prev stats.
985088
-	  *)
985088
-	 let devs : (string * string * rd_active *
985088
-		       D.interface_stats * D.interface_stats) list =
985088
-	   List.flatten devs in
985088
-
985088
-	 (* Difference curr slice & prev slice. *)
985088
-	 let devs = List.map (
985088
-	   fun (dev, name, rd, curr, prev) ->
985088
-	     dev, name, rd, diff_interface_stats curr prev
985088
-	 ) devs in
985088
-
985088
-	 (* Sort by current sort order, but map some of the standard
985088
-	  * sort orders into ones which makes sense here.
985088
-	  *)
985088
-	 let devs =
985088
-	   let cmp =
985088
-	     match !sort_order with
985088
-	     | DomainName ->
985088
-		 (fun _ -> 0) (* fallthrough to default name compare *)
985088
-	     | DomainID ->
985088
-		 (fun (_, { rd_domid = id1 }, _, { rd_domid = id2 }) ->
985088
-		    compare id1 id2)
985088
-	     | Processor | Memory | Time | BlockRdRq | BlockWrRq
985088
-		   (* fallthrough to RXBY comparison. *)
985088
-	     | NetRX ->
985088
-		 (fun ({ D.rx_bytes = b1 }, _, { D.rx_bytes = b2 }, _) ->
985088
-		    compare b2 b1)
985088
-	     | NetTX ->
985088
-		 (fun ({ D.tx_bytes = b1 }, _, { D.tx_bytes = b2 }, _) ->
985088
-		    compare b2 b1)
985088
-	   in
985088
-	   let cmp (dev1, name1, rd1, stats1) (dev2, name2, rd2, stats2) =
985088
-	     let r = cmp (stats1, rd1, stats2, rd2) in
985088
-	     if r <> 0 then r
985088
-	     else compare (dev1, name1) (dev2, name2)
985088
-	   in
985088
-	   List.sort ~cmp devs in
985088
-
985088
-	 (* Print the header for network devices. *)
985088
-	 attron A.reverse;
985088
-	 mvaddstr header_lineno 0
985088
-	   (pad cols "   ID S RXBY TXBY RXPK TXPK DOMAIN       INTERFACE");
985088
-	 attroff A.reverse;
985088
-
985088
-	 (* Print domains and devices. *)
985088
-	 let rec loop lineno = function
985088
-	   | [] -> ()
985088
-	   | (dev, name, rd, stats) :: devs ->
985088
-	       if lineno < lines then (
985088
-		 let state = show_state rd.rd_info.D.state in
985088
-		 let rx_bytes =
985088
-		   if stats.D.rx_bytes >= 0L
985088
-		   then Show.int64 stats.D.rx_bytes
985088
-		   else "    " in
985088
-		 let tx_bytes =
985088
-		   if stats.D.tx_bytes >= 0L
985088
-		   then Show.int64 stats.D.tx_bytes
985088
-		   else "    " in
985088
-		 let rx_packets =
985088
-		   if stats.D.rx_packets >= 0L
985088
-		   then Show.int64 stats.D.rx_packets
985088
-		   else "    " in
985088
-		 let tx_packets =
985088
-		   if stats.D.tx_packets >= 0L
985088
-		   then Show.int64 stats.D.tx_packets
985088
-		   else "    " in
985088
-
985088
-		 let line = sprintf "%5d %c %s %s %s %s %-12s %s"
985088
-		   rd.rd_domid state
985088
-		   rx_bytes tx_bytes
985088
-		   rx_packets tx_packets
985088
-		   (pad 12 name) dev in
985088
-		 let line = pad cols line in
985088
-		 mvaddstr lineno 0 line;
985088
-		 loop (lineno+1) devs
985088
-	       )
985088
-	 in
985088
-	 loop domains_lineno devs
985088
-
985088
-     | BlockDisplay -> (*---------- Showing block devices ----------*)
985088
-	 (* Only care about active domains. *)
985088
-	 let doms = List.filter_map (
985088
-	   function
985088
-	   | (name, Active rd) -> Some (name, rd)
985088
-	   | (_, Inactive) -> None
985088
-	 ) doms in
985088
-
985088
-	 (* For each domain we have a list of block devices seen
985088
-	  * this slice, and seen in the previous slice, which we now
985088
-	  * match up to get a list of (domain, device) for which
985088
-	  * we have current & previous knowledge.  (And ignore the rest).
985088
-	  *)
985088
-	 let devs =
985088
-	   List.map (
985088
-	     fun (name, rd) ->
985088
-	       List.filter_map (
985088
-		 fun (dev, stats) ->
985088
-		   try
985088
-		     (* Have prev slice stats for this device? *)
985088
-		     let prev_stats =
985088
-		       List.assoc dev rd.rd_prev_block_stats in
985088
-		     Some (dev, name, rd, stats, prev_stats)
985088
-		   with Not_found -> None
985088
-	       ) rd.rd_block_stats
985088
-	   ) doms in
985088
-
985088
-	 (* Finally we have a list of:
985088
-	  * device name, domain name, rd_* stuff, curr stats, prev stats.
985088
-	  *)
985088
-	 let devs : (string * string * rd_active *
985088
-		       D.block_stats * D.block_stats) list =
985088
-	   List.flatten devs in
985088
-
985088
-	 (* Difference curr slice & prev slice. *)
985088
-	 let devs = List.map (
985088
-	   fun (dev, name, rd, curr, prev) ->
985088
-	     dev, name, rd, diff_block_stats curr prev
985088
-	 ) devs in
985088
-
985088
-	 (* Sort by current sort order, but map some of the standard
985088
-	  * sort orders into ones which makes sense here.
985088
-	  *)
985088
-	 let devs =
985088
-	   let cmp =
985088
-	     match !sort_order with
985088
-	     | DomainName ->
985088
-		 (fun _ -> 0) (* fallthrough to default name compare *)
985088
-	     | DomainID ->
985088
-		 (fun (_, { rd_domid = id1 }, _, { rd_domid = id2 }) ->
985088
-		    compare id1 id2)
985088
-	     | Processor | Memory | Time | NetRX | NetTX
985088
-		   (* fallthrough to RDRQ comparison. *)
985088
-	     | BlockRdRq ->
985088
-		 (fun ({ D.rd_req = b1 }, _, { D.rd_req = b2 }, _) ->
985088
-		    compare b2 b1)
985088
-	     | BlockWrRq ->
985088
-		 (fun ({ D.wr_req = b1 }, _, { D.wr_req = b2 }, _) ->
985088
-		    compare b2 b1)
985088
-	   in
985088
-	   let cmp (dev1, name1, rd1, stats1) (dev2, name2, rd2, stats2) =
985088
-	     let r = cmp (stats1, rd1, stats2, rd2) in
985088
-	     if r <> 0 then r
985088
-	     else compare (dev1, name1) (dev2, name2)
985088
-	   in
985088
-	   List.sort ~cmp devs in
985088
-
985088
-	 (* Print the header for block devices. *)
985088
-	 attron A.reverse;
985088
-	 mvaddstr header_lineno 0
985088
-	   (pad cols "   ID S RDBY WRBY RDRQ WRRQ DOMAIN       DEVICE");
985088
-	 attroff A.reverse;
985088
-
985088
-	 (* Print domains and devices. *)
985088
-	 let rec loop lineno = function
985088
-	   | [] -> ()
985088
-	   | (dev, name, rd, stats) :: devs ->
985088
-	       if lineno < lines then (
985088
-		 let state = show_state rd.rd_info.D.state in
985088
-		 let rd_bytes =
985088
-		   if stats.D.rd_bytes >= 0L
985088
-		   then Show.int64 stats.D.rd_bytes
985088
-		   else "    " in
985088
-		 let wr_bytes =
985088
-		   if stats.D.wr_bytes >= 0L
985088
-		   then Show.int64 stats.D.wr_bytes
985088
-		   else "    " in
985088
-		 let rd_req =
985088
-		   if stats.D.rd_req >= 0L
985088
-		   then Show.int64 stats.D.rd_req
985088
-		   else "    " in
985088
-		 let wr_req =
985088
-		   if stats.D.wr_req >= 0L
985088
-		   then Show.int64 stats.D.wr_req
985088
-		   else "    " in
985088
-
985088
-		 let line = sprintf "%5d %c %s %s %s %s %-12s %s"
985088
-		   rd.rd_domid state
985088
-		   rd_bytes wr_bytes
985088
-		   rd_req wr_req
985088
-		   (pad 12 name) dev in
985088
-		 let line = pad cols line in
985088
-		 mvaddstr lineno 0 line;
985088
-		 loop (lineno+1) devs
985088
-	       )
985088
-	 in
985088
-	 loop domains_lineno devs
985088
-    ); (* end of display_mode conditional section *)
985088
-
985088
-    let (count, running, blocked, paused, shutdown, shutoff,
985088
-	 crashed, active, inactive,
985088
-	 total_cpu_time, total_memory, total_domU_memory) = totals in
985088
-
985088
-    mvaddstr summary_lineno 0
985088
-      (sprintf
985088
-	 (f_"%d domains, %d active, %d running, %d sleeping, %d paused, %d inactive D:%d O:%d X:%d")
985088
-	 count active running blocked paused inactive shutdown shutoff crashed);
985088
-
985088
-    (* Total %CPU used, and memory summary. *)
985088
-    let percent_cpu = 100. *. total_cpu_time /. total_cpu in
985088
-    mvaddstr (summary_lineno+1) 0
985088
-      (sprintf
985088
-	 (f_"CPU: %2.1f%%  Mem: %Ld MB (%Ld MB by guests)")
985088
-	 percent_cpu (total_memory /^ 1024L) (total_domU_memory /^ 1024L));
985088
-
985088
-    (* Time to grab another historical %CPU for the list? *)
985088
-    if time >= !historical_cpu_last_time +. float !historical_cpu_delay
985088
-    then (
985088
-      historical_cpu := percent_cpu :: List.take 10 !historical_cpu;
985088
-      historical_cpu_last_time := time
985088
-    );
985088
-
985088
-    (* Display historical CPU time. *)
985088
-    let () =
985088
-      let y, x = historical_cursor in
985088
-      let maxwidth = cols - x in
985088
-      let line =
985088
-	String.concat " "
985088
-	  (List.map (sprintf "%2.1f%%") !historical_cpu) in
985088
-      let line = pad maxwidth line in
985088
-      mvaddstr y x line;
985088
-      () in
985088
-
985088
-    move message_lineno 0; (* Park cursor in message area, as with top. *)
985088
-    refresh ()		   (* Refresh the display. *)
985088
-
985088
-(* Write CSV header row. *)
985088
-let write_csv_header () =
985088
-  (!csv_write) (
985088
-    [ "Hostname"; "Time"; "Arch"; "Physical CPUs";
985088
-      "Count"; "Running"; "Blocked"; "Paused"; "Shutdown";
985088
-      "Shutoff"; "Crashed"; "Active"; "Inactive";
985088
-      "%CPU";
985088
-      "Total hardware memory (KB)";
985088
-      "Total memory (KB)"; "Total guest memory (KB)";
985088
-      "Total CPU time (ns)" ] @
985088
-      (* These fields are repeated for each domain: *)
985088
-    [ "Domain ID"; "Domain name"; ] @
985088
-    (if !csv_cpu then [ "CPU (ns)"; "%CPU"; ] else []) @
985088
-    (if !csv_mem then [ "Mem (bytes)"; "%Mem";] else []) @
985088
-    (if !csv_block && not !block_in_bytes
985088
-       then [ "Block RDRQ"; "Block WRRQ"; ] else []) @
985088
-    (if !csv_block && !block_in_bytes
985088
-       then [ "Block RDBY"; "Block WRBY"; ] else []) @
985088
-    (if !csv_net then [ "Net RXBY"; "Net TXBY" ] else [])
985088
-  )
985088
-
985088
-(* Write summary data to CSV file. *)
985088
-let append_csv
985088
-    (_, _, _, _, _, node_info, hostname, _) (* setup *)
985088
-    (doms,
985088
-     _, printable_time,
985088
-     nr_pcpus, total_cpu, _,
985088
-     totals,
985088
-     _) (* state *) =
985088
-
985088
-  (* The totals / summary fields. *)
985088
-  let (count, running, blocked, paused, shutdown, shutoff,
985088
-       crashed, active, inactive,
985088
-       total_cpu_time, total_memory, total_domU_memory) = totals in
985088
-
985088
-  let percent_cpu = 100. *. total_cpu_time /. total_cpu in
985088
-
985088
-  let summary_fields = [
985088
-    hostname; printable_time; node_info.C.model; string_of_int nr_pcpus;
985088
-    string_of_int count; string_of_int running; string_of_int blocked;
985088
-    string_of_int paused; string_of_int shutdown; string_of_int shutoff;
985088
-    string_of_int crashed; string_of_int active; string_of_int inactive;
985088
-    sprintf "%2.1f" percent_cpu;
985088
-    Int64.to_string node_info.C.memory;
985088
-    Int64.to_string total_memory; Int64.to_string total_domU_memory;
985088
-    Int64.to_string (Int64.of_float total_cpu_time)
985088
-  ] in
985088
-
985088
-  (* The domains.
985088
-   *
985088
-   * Sort them by ID so that the list of relatively stable.  Ignore
985088
-   * inactive domains.
985088
-   *)
985088
-  let doms = List.filter_map (
985088
-    function
985088
-    | _, Inactive -> None		(* Ignore inactive domains. *)
985088
-    | name, Active rd -> Some (name, rd)
985088
-  ) doms in
985088
-  let cmp (_, { rd_domid = rd_domid1 }) (_, { rd_domid = rd_domid2 }) =
985088
-    compare rd_domid1 rd_domid2
985088
-  in
985088
-  let doms = List.sort ~cmp doms in
985088
-
985088
-  let string_of_int64_option = Option.map_default Int64.to_string "" in
985088
-
985088
-  let domain_fields = List.map (
985088
-    fun (domname, rd) ->
985088
-      [ string_of_int rd.rd_domid; domname ] @
985088
-	(if !csv_cpu then [
985088
-	   string_of_float rd.rd_cpu_time; string_of_float rd.rd_percent_cpu
985088
-	 ] else []) @
985088
-        (if !csv_mem then [
985088
-            Int64.to_string rd.rd_mem_bytes; Int64.to_string rd.rd_mem_percent
985088
-         ] else []) @
985088
-	(if !csv_block then [
985088
-	   string_of_int64_option rd.rd_block_rd_info;
985088
-	   string_of_int64_option rd.rd_block_wr_info;
985088
-	 ] else []) @
985088
-	(if !csv_net then [
985088
-	   string_of_int64_option rd.rd_net_rx_bytes;
985088
-	   string_of_int64_option rd.rd_net_tx_bytes;
985088
-	 ] else [])
985088
-  ) doms in
985088
-  let domain_fields = List.flatten domain_fields in
985088
-
985088
-  (!csv_write) (summary_fields @ domain_fields)
985088
-
985088
-let dump_stdout
985088
-    (_, _, _, _, _, node_info, hostname, _) (* setup *)
985088
-    (doms,
985088
-     _, printable_time,
985088
-     nr_pcpus, total_cpu, _,
985088
-     totals,
985088
-     _) (* state *) =
985088
-
985088
-  (* Header for this iteration *)
985088
-  printf "virt-top time  %s Host %s %s %d/%dCPU %dMHz %LdMB \n"
985088
-    printable_time hostname node_info.C.model node_info.C.cpus nr_pcpus
985088
-    node_info.C.mhz (node_info.C.memory /^ 1024L);
985088
-  (* dump domain information one by one *)
985088
-   let rd, wr = if !block_in_bytes then "RDBY", "WRBY" else "RDRQ", "WRRQ"
985088
-   in
985088
-     printf "   ID S %s %s RXBY TXBY %%CPU %%MEM   TIME    NAME\n" rd wr;
985088
-
985088
-  (* sort by ID *)
985088
-  let doms =
985088
-    let compare =
985088
-      (function
985088
-       | Active {rd_domid = id1 }, Active {rd_domid = id2} ->
985088
-           compare id1 id2
985088
-       | Active _, Inactive -> -1
985088
-       | Inactive, Active _ -> 1
985088
-       | Inactive, Inactive -> 0)
985088
-    in
985088
-    let cmp  (name1, dom1) (name2, dom2) = compare(dom1, dom2) in
985088
-    List.sort ~cmp doms in
985088
-  (*Print domains *)
985088
-  let dump_domain = fun name rd
985088
-  -> begin
985088
-    let state = show_state rd.rd_info.D.state in
985088
-         let rd_req = if rd.rd_block_rd_info = None then "   0"
985088
-                      else Show.int64_option rd.rd_block_rd_info in
985088
-         let wr_req = if rd.rd_block_wr_info = None then "   0"
985088
-                      else Show.int64_option rd.rd_block_wr_info in
985088
-    let rx_bytes = if rd.rd_net_rx_bytes = None then "   0"
985088
-    else Show.int64_option rd.rd_net_rx_bytes in
985088
-    let tx_bytes = if rd.rd_net_tx_bytes = None then "   0"
985088
-    else Show.int64_option rd.rd_net_tx_bytes in
985088
-    let percent_cpu = Show.percent rd.rd_percent_cpu in
985088
-    let percent_mem = Int64.to_float rd.rd_mem_percent in
985088
-    let percent_mem = Show.percent percent_mem in
985088
-    let time = Show.time rd.rd_info.D.cpu_time in
985088
-    printf "%5d %c %s %s %s %s %s %s %s %s\n"
985088
-      rd.rd_domid state rd_req wr_req rx_bytes tx_bytes
985088
-      percent_cpu percent_mem time name;
985088
-  end
985088
-  in
985088
-  List.iter (
985088
-    function
985088
-    | name, Active dom -> dump_domain name dom
985088
-    | name, Inactive -> ()
985088
-  ) doms;
985088
-  flush stdout
985088
-
985088
 (* Main loop. *)
985088
 let rec main_loop ((_, batch_mode, script_mode, csv_enabled, stream_mode, _, _, _)
985088
 		     as setup) =
985088
-  if csv_enabled then write_csv_header ();
985088
+  let csv_flags = !csv_cpu, !csv_mem, !csv_block, !csv_net in
985088
+
985088
+  if csv_enabled then
985088
+    Csv_output.write_csv_header csv_flags !block_in_bytes;
985088
 
985088
   while not !quit do
985088
-    let state = collect setup in	        (* Collect stats. *)
985088
+    (* Collect stats. *)
985088
+    let state = collect setup !block_in_bytes in
985088
+    let pcpu_display =
985088
+      if !display_mode = PCPUDisplay then Some (collect_pcpu state)
985088
+      else None in
985088
     (* Redraw display. *)
985088
-    if not script_mode && not stream_mode then redraw setup state;
985088
-    if csv_enabled then append_csv setup state; (* Update CSV file. *)
985088
-    if stream_mode then dump_stdout setup state; (* dump to stdout *)
985088
+    if not script_mode && not stream_mode then
985088
+      Redraw.redraw !display_mode !sort_order
985088
+                    setup !block_in_bytes !historical_cpu_delay
985088
+                    state pcpu_display;
985088
+
985088
+    (* Update CSV file. *)
985088
+    if csv_enabled then
985088
+      Csv_output.append_csv setup csv_flags state;
985088
+
985088
+    (* Append to stream output file. *)
985088
+    if stream_mode then
985088
+      Stream_output.append_stream setup !block_in_bytes state;
985088
 
985088
     (* Clear up unused virDomainPtr objects. *)
985088
     Gc.compact ();
985088
@@ -1440,11 +356,10 @@ let rec main_loop ((_, batch_mode, script_mode, csv_enabled, stream_mode, _, _,
985088
           (* No --end-time option, so use the current delay. *)
985088
           !delay
985088
       | Some end_time ->
985088
-	  let (_, time, _, _, _, _, _, _) = state in
985088
 	  let delay_secs = float !delay /. 1000. in
985088
-	  if end_time <= time +. delay_secs then (
985088
+	  if end_time <= state.rd_time +. delay_secs then (
985088
             quit := true;
985088
-            let delay = int_of_float (1000. *. (end_time -. time)) in
985088
+            let delay = int_of_float (1000. *. (end_time -. state.rd_time)) in
985088
             if delay >= 0 then delay else 0
985088
           ) else
985088
             !delay in
985088
diff --git a/src/top.mli b/src/top.mli
985088
index b0953dd..b625910 100644
985088
--- a/src/top.mli
985088
+++ b/src/top.mli
985088
@@ -1,5 +1,5 @@
985088
 (* 'top'-like tool for libvirt domains.
985088
-   (C) Copyright 2007-2009 Richard W.M. Jones, Red Hat Inc.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
    http://libvirt.org/
985088
 
985088
    This program is free software; you can redistribute it and/or modify
985088
@@ -17,23 +17,11 @@
985088
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
 *)
985088
 
985088
-(* Hook for [Opt_xml] to override (if present). *)
985088
-val parse_device_xml :
985088
-  (int -> [ `R ] Libvirt.Domain.t -> string list * string list) ref
985088
-
985088
-(* Hooks for [Opt_csv] to override (if present). *)
985088
+(* Hook for [Opt_csv] to override (if present). *)
985088
 val csv_start : (string -> unit) ref
985088
-val csv_write : (string list -> unit) ref
985088
 
985088
 (* Hook for [Opt_calendar] to override (if present). *)
985088
 val parse_date_time : (string -> float) ref
985088
 
985088
-type setup =
985088
-    Libvirt.ro Libvirt.Connect.t	(* connection *)
985088
-    * bool * bool * bool * bool		(* batch, script, csv, stream mode *)
985088
-    * Libvirt.Connect.node_info		(* node_info *)
985088
-    * string				(* hostname *)
985088
-    * (int * int * int)			(* libvirt version *)
985088
-
985088
-val start_up : unit -> setup
985088
-val main_loop : setup -> unit
985088
+val start_up : unit -> Types.setup
985088
+val main_loop : Types.setup -> unit
985088
diff --git a/src/types.ml b/src/types.ml
985088
new file mode 100644
985088
index 0000000..2fdd49b
985088
--- /dev/null
985088
+++ b/src/types.ml
985088
@@ -0,0 +1,147 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+open Opt_gettext.Gettext
985088
+open Utils
985088
+
985088
+module C = Libvirt.Connect
985088
+module D = Libvirt.Domain
985088
+
985088
+(* XXX We should get rid of this type. *)
985088
+type setup =
985088
+    Libvirt.ro C.t              (* connection *)
985088
+    * bool * bool * bool * bool (* batch, script, csv, stream mode *)
985088
+    * C.node_info		(* node_info *)
985088
+    * string                    (* hostname *)
985088
+    * (int * int * int)         (* libvirt version *)
985088
+
985088
+(* Sort order. *)
985088
+type sort_order =
985088
+  | DomainID | DomainName | Processor | Memory | Time
985088
+  | NetRX | NetTX | BlockRdRq | BlockWrRq
985088
+let all_sort_fields = [
985088
+  DomainID; DomainName; Processor; Memory; Time;
985088
+  NetRX; NetTX; BlockRdRq; BlockWrRq
985088
+]
985088
+let printable_sort_order = function
985088
+  | Processor -> s_"%CPU"
985088
+  | Memory -> s_"%MEM"
985088
+  | Time -> s_"TIME (CPU time)"
985088
+  | DomainID -> s_"Domain ID"
985088
+  | DomainName -> s_"Domain name"
985088
+  | NetRX -> s_"Net RX bytes"
985088
+  | NetTX -> s_"Net TX bytes"
985088
+  | BlockRdRq -> s_"Block read reqs"
985088
+  | BlockWrRq -> s_"Block write reqs"
985088
+let sort_order_of_cli = function
985088
+  | "cpu" | "processor" -> Processor
985088
+  | "mem" | "memory" -> Memory
985088
+  | "time" -> Time
985088
+  | "id" -> DomainID
985088
+  | "name" -> DomainName
985088
+  | "netrx" -> NetRX | "nettx" -> NetTX
985088
+  | "blockrdrq" -> BlockRdRq | "blockwrrq" -> BlockWrRq
985088
+  | str ->
985088
+      failwithf (f_"%s: sort order should be: %s")
985088
+	str "cpu|mem|time|id|name|netrx|nettx|blockrdrq|blockwrrq"
985088
+let cli_of_sort_order = function
985088
+  | Processor -> "cpu"
985088
+  | Memory -> "mem"
985088
+  | Time -> "time"
985088
+  | DomainID -> "id"
985088
+  | DomainName -> "name"
985088
+  | NetRX -> "netrx"
985088
+  | NetTX -> "nettx"
985088
+  | BlockRdRq -> "blockrdrq"
985088
+  | BlockWrRq -> "blockwrrq"
985088
+
985088
+(* Current major display mode: TaskDisplay is the normal display. *)
985088
+type display = TaskDisplay | PCPUDisplay | BlockDisplay | NetDisplay
985088
+
985088
+let display_of_cli = function
985088
+  | "task" -> TaskDisplay
985088
+  | "pcpu" -> PCPUDisplay
985088
+  | "block" -> BlockDisplay
985088
+  | "net" -> NetDisplay
985088
+  | str ->
985088
+      failwithf (f_"%s: display should be %s") str "task|pcpu|block|net"
985088
+let cli_of_display = function
985088
+  | TaskDisplay -> "task"
985088
+  | PCPUDisplay -> "pcpu"
985088
+  | BlockDisplay -> "block"
985088
+  | NetDisplay -> "net"
985088
+
985088
+(* Sum Domain.block_stats structures together.  Missing fields
985088
+ * get forced to 0.  Empty list returns all 0.
985088
+ *)
985088
+let zero_block_stats =
985088
+  { D.rd_req = 0L; rd_bytes = 0L; wr_req = 0L; wr_bytes = 0L; errs = 0L }
985088
+let add_block_stats bs1 bs2 =
985088
+  let add f1 f2 = if f1 >= 0L && f2 >= 0L then f1 +^ f2 else 0L in
985088
+  { D.rd_req = add bs1.D.rd_req   bs2.D.rd_req;
985088
+    rd_bytes = add bs1.D.rd_bytes bs2.D.rd_bytes;
985088
+    wr_req   = add bs1.D.wr_req   bs2.D.wr_req;
985088
+    wr_bytes = add bs1.D.wr_bytes bs2.D.wr_bytes;
985088
+    errs     = add bs1.D.errs     bs2.D.errs }
985088
+let sum_block_stats =
985088
+  List.fold_left add_block_stats zero_block_stats
985088
+
985088
+(* Get the difference between two block_stats structures.  Missing data
985088
+ * forces the difference to -1.
985088
+ *)
985088
+let diff_block_stats curr prev =
985088
+  let sub f1 f2 = if f1 >= 0L && f2 >= 0L then f1 -^ f2 else -1L in
985088
+  { D.rd_req = sub curr.D.rd_req   prev.D.rd_req;
985088
+    rd_bytes = sub curr.D.rd_bytes prev.D.rd_bytes;
985088
+    wr_req   = sub curr.D.wr_req   prev.D.wr_req;
985088
+    wr_bytes = sub curr.D.wr_bytes prev.D.wr_bytes;
985088
+    errs     = sub curr.D.errs     prev.D.errs }
985088
+
985088
+(* Sum Domain.interface_stats structures together.  Missing fields
985088
+ * get forced to 0.  Empty list returns all 0.
985088
+ *)
985088
+let zero_interface_stats =
985088
+  { D.rx_bytes = 0L; rx_packets = 0L; rx_errs = 0L; rx_drop = 0L;
985088
+    tx_bytes = 0L; tx_packets = 0L; tx_errs = 0L; tx_drop = 0L }
985088
+let add_interface_stats is1 is2 =
985088
+  let add f1 f2 = if f1 >= 0L && f2 >= 0L then f1 +^ f2 else 0L in
985088
+  { D.rx_bytes = add is1.D.rx_bytes   is2.D.rx_bytes;
985088
+    rx_packets = add is1.D.rx_packets is2.D.rx_packets;
985088
+    rx_errs    = add is1.D.rx_errs    is2.D.rx_errs;
985088
+    rx_drop    = add is1.D.rx_drop    is2.D.rx_drop;
985088
+    tx_bytes   = add is1.D.tx_bytes   is2.D.tx_bytes;
985088
+    tx_packets = add is1.D.tx_packets is2.D.tx_packets;
985088
+    tx_errs    = add is1.D.tx_errs    is2.D.tx_errs;
985088
+    tx_drop    = add is1.D.tx_drop    is2.D.tx_drop }
985088
+let sum_interface_stats =
985088
+  List.fold_left add_interface_stats zero_interface_stats
985088
+
985088
+(* Get the difference between two interface_stats structures.
985088
+ * Missing data forces the difference to -1.
985088
+ *)
985088
+let diff_interface_stats curr prev =
985088
+  let sub f1 f2 = if f1 >= 0L && f2 >= 0L then f1 -^ f2 else -1L in
985088
+  { D.rx_bytes = sub curr.D.rx_bytes   prev.D.rx_bytes;
985088
+    rx_packets = sub curr.D.rx_packets prev.D.rx_packets;
985088
+    rx_errs    = sub curr.D.rx_errs    prev.D.rx_errs;
985088
+    rx_drop    = sub curr.D.rx_drop    prev.D.rx_drop;
985088
+    tx_bytes   = sub curr.D.tx_bytes   prev.D.tx_bytes;
985088
+    tx_packets = sub curr.D.tx_packets prev.D.tx_packets;
985088
+    tx_errs    = sub curr.D.tx_errs    prev.D.tx_errs;
985088
+    tx_drop    = sub curr.D.tx_drop    prev.D.tx_drop }
985088
diff --git a/src/types.mli b/src/types.mli
985088
new file mode 100644
985088
index 0000000..6297482
985088
--- /dev/null
985088
+++ b/src/types.mli
985088
@@ -0,0 +1,49 @@
985088
+(* 'top'-like tool for libvirt domains.
985088
+   (C) Copyright 2007-2017 Richard W.M. Jones, Red Hat Inc.
985088
+   http://libvirt.org/
985088
+
985088
+   This program is free software; you can redistribute it and/or modify
985088
+   it under the terms of the GNU General Public License as published by
985088
+   the Free Software Foundation; either version 2 of the License, or
985088
+   (at your option) any later version.
985088
+
985088
+   This program is distributed in the hope that it will be useful,
985088
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
985088
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
985088
+   GNU General Public License for more details.
985088
+
985088
+   You should have received a copy of the GNU General Public License
985088
+   along with this program; if not, write to the Free Software
985088
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
985088
+*)
985088
+
985088
+(* XXX We should get rid of this type. *)
985088
+type setup =
985088
+    Libvirt.ro Libvirt.Connect.t	(* connection *)
985088
+    * bool * bool * bool * bool		(* batch, script, csv, stream mode *)
985088
+    * Libvirt.Connect.node_info		(* node_info *)
985088
+    * string				(* hostname *)
985088
+    * (int * int * int)			(* libvirt version *)
985088
+
985088
+(* Sort order. *)
985088
+type sort_order =
985088
+  | DomainID | DomainName | Processor | Memory | Time
985088
+  | NetRX | NetTX | BlockRdRq | BlockWrRq
985088
+
985088
+val all_sort_fields : sort_order list
985088
+val printable_sort_order : sort_order -> string
985088
+val sort_order_of_cli : string -> sort_order
985088
+val cli_of_sort_order : sort_order -> string
985088
+
985088
+(* Current major display mode: TaskDisplay is the normal display. *)
985088
+type display = TaskDisplay | PCPUDisplay | BlockDisplay | NetDisplay
985088
+
985088
+val display_of_cli : string -> display
985088
+val cli_of_display : display -> string
985088
+
985088
+(* Helpers for manipulating block_stats & interface_stats. *)
985088
+val sum_block_stats : Libvirt.Domain.block_stats list -> Libvirt.Domain.block_stats
985088
+val diff_block_stats : Libvirt.Domain.block_stats -> Libvirt.Domain.block_stats -> Libvirt.Domain.block_stats
985088
+
985088
+val sum_interface_stats : Libvirt.Domain.interface_stats list -> Libvirt.Domain.interface_stats
985088
+val diff_interface_stats : Libvirt.Domain.interface_stats -> Libvirt.Domain.interface_stats -> Libvirt.Domain.interface_stats
985088
diff --git a/src/utils.ml b/src/utils.ml
985088
index 3dc637d..5fcc905 100644
985088
--- a/src/utils.ml
985088
+++ b/src/utils.ml
985088
@@ -21,12 +21,6 @@
985088
 
985088
 open Printf
985088
 
985088
-open Opt_gettext.Gettext
985088
-
985088
-module C = Libvirt.Connect
985088
-module D = Libvirt.Domain
985088
-module N = Libvirt.Network
985088
-
985088
 let (//) = Filename.concat
985088
 
985088
 (* Int64 operators for convenience. *)
985088
@@ -166,62 +160,3 @@ module Show = struct
985088
       sprintf "%3Ldd%02Ld:%02Ld" days hours mins
985088
     )
985088
 end
985088
-
985088
-(* Sum Domain.block_stats structures together.  Missing fields
985088
- * get forced to 0.  Empty list returns all 0.
985088
- *)
985088
-let zero_block_stats =
985088
-  { D.rd_req = 0L; rd_bytes = 0L; wr_req = 0L; wr_bytes = 0L; errs = 0L }
985088
-let add_block_stats bs1 bs2 =
985088
-  let add f1 f2 = if f1 >= 0L && f2 >= 0L then f1 +^ f2 else 0L in
985088
-  { D.rd_req = add bs1.D.rd_req   bs2.D.rd_req;
985088
-    rd_bytes = add bs1.D.rd_bytes bs2.D.rd_bytes;
985088
-    wr_req   = add bs1.D.wr_req   bs2.D.wr_req;
985088
-    wr_bytes = add bs1.D.wr_bytes bs2.D.wr_bytes;
985088
-    errs     = add bs1.D.errs     bs2.D.errs }
985088
-let sum_block_stats =
985088
-  List.fold_left add_block_stats zero_block_stats
985088
-
985088
-(* Get the difference between two block_stats structures.  Missing data
985088
- * forces the difference to -1.
985088
- *)
985088
-let diff_block_stats curr prev =
985088
-  let sub f1 f2 = if f1 >= 0L && f2 >= 0L then f1 -^ f2 else -1L in
985088
-  { D.rd_req = sub curr.D.rd_req   prev.D.rd_req;
985088
-    rd_bytes = sub curr.D.rd_bytes prev.D.rd_bytes;
985088
-    wr_req   = sub curr.D.wr_req   prev.D.wr_req;
985088
-    wr_bytes = sub curr.D.wr_bytes prev.D.wr_bytes;
985088
-    errs     = sub curr.D.errs     prev.D.errs }
985088
-
985088
-(* Sum Domain.interface_stats structures together.  Missing fields
985088
- * get forced to 0.  Empty list returns all 0.
985088
- *)
985088
-let zero_interface_stats =
985088
-  { D.rx_bytes = 0L; rx_packets = 0L; rx_errs = 0L; rx_drop = 0L;
985088
-    tx_bytes = 0L; tx_packets = 0L; tx_errs = 0L; tx_drop = 0L }
985088
-let add_interface_stats is1 is2 =
985088
-  let add f1 f2 = if f1 >= 0L && f2 >= 0L then f1 +^ f2 else 0L in
985088
-  { D.rx_bytes = add is1.D.rx_bytes   is2.D.rx_bytes;
985088
-    rx_packets = add is1.D.rx_packets is2.D.rx_packets;
985088
-    rx_errs    = add is1.D.rx_errs    is2.D.rx_errs;
985088
-    rx_drop    = add is1.D.rx_drop    is2.D.rx_drop;
985088
-    tx_bytes   = add is1.D.tx_bytes   is2.D.tx_bytes;
985088
-    tx_packets = add is1.D.tx_packets is2.D.tx_packets;
985088
-    tx_errs    = add is1.D.tx_errs    is2.D.tx_errs;
985088
-    tx_drop    = add is1.D.tx_drop    is2.D.tx_drop }
985088
-let sum_interface_stats =
985088
-  List.fold_left add_interface_stats zero_interface_stats
985088
-
985088
-(* Get the difference between two interface_stats structures.
985088
- * Missing data forces the difference to -1.
985088
- *)
985088
-let diff_interface_stats curr prev =
985088
-  let sub f1 f2 = if f1 >= 0L && f2 >= 0L then f1 -^ f2 else -1L in
985088
-  { D.rx_bytes = sub curr.D.rx_bytes   prev.D.rx_bytes;
985088
-    rx_packets = sub curr.D.rx_packets prev.D.rx_packets;
985088
-    rx_errs    = sub curr.D.rx_errs    prev.D.rx_errs;
985088
-    rx_drop    = sub curr.D.rx_drop    prev.D.rx_drop;
985088
-    tx_bytes   = sub curr.D.tx_bytes   prev.D.tx_bytes;
985088
-    tx_packets = sub curr.D.tx_packets prev.D.tx_packets;
985088
-    tx_errs    = sub curr.D.tx_errs    prev.D.tx_errs;
985088
-    tx_drop    = sub curr.D.tx_drop    prev.D.tx_drop }
985088
diff --git a/src/utils.mli b/src/utils.mli
985088
index 5b71b31..6e81215 100644
985088
--- a/src/utils.mli
985088
+++ b/src/utils.mli
985088
@@ -46,12 +46,3 @@ module Show : sig
985088
   val int64 : int64 -> string
985088
   val time : int64 -> string
985088
 end
985088
-
985088
-(* Helpers for manipulating block_stats & interface_stats. *)
985088
-open Libvirt.Domain
985088
-
985088
-val sum_block_stats : block_stats list -> block_stats
985088
-val diff_block_stats : block_stats -> block_stats -> block_stats
985088
-
985088
-val sum_interface_stats : interface_stats list -> interface_stats
985088
-val diff_interface_stats : interface_stats -> interface_stats -> interface_stats
985088
-- 
985088
2.9.3
985088