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

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