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

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