+ (* Collect some extra information in PCPUDisplay display_mode. *)
+ let pcpu_display =
+ if !display_mode = PCPUDisplay then (
+ (* Get the VCPU info and VCPU->PCPU mappings for active domains.
+ * Also cull some data we don't care about.
+ *)
+ let doms = List.filter_map (
+ function
+ | (name, Active rd) ->
+ (try
+ let domid = rd.rd_domid in
+ let maplen = C.cpumaplen nr_pcpus in
+ let maxinfo = rd.rd_info.D.nr_virt_cpu in
+ let nr_vcpus, vcpu_infos, cpumaps =
+ D.get_vcpus rd.rd_dom maxinfo maplen in
+
+ (* Got previous vcpu_infos for this domain? *)
+ let prev_vcpu_infos =
+ try Some (Hashtbl.find last_vcpu_info domid)
+ with Not_found -> None in
+ (* Update last_vcpu_info. *)
+ Hashtbl.replace last_vcpu_info domid vcpu_infos;
+
+ (match prev_vcpu_infos with
+ | Some prev_vcpu_infos
+ when Array.length prev_vcpu_infos = Array.length vcpu_infos ->
+ Some (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos,
+ cpumaps, maplen)
+ | _ -> None (* ignore missing / unequal length prev_vcpu_infos *)
+ );
+ with
+ Libvirt.Virterror _ -> None(* ignore transient libvirt errs *)
+ )
+ | (_, Inactive) -> None (* ignore inactive doms *)
+ ) doms in
+ let nr_doms = List.length doms in
+
+ (* Rearrange the data into a matrix. Major axis (down) is
+ * pCPUs. Minor axis (right) is domains. At each node we store:
+ * cpu_time (on this pCPU only, nanosecs),
+ * average? (if set, then cpu_time is an average because the
+ * vCPU is pinned to more than one pCPU)
+ * running? (if set, we were instantaneously running on this pCPU)
+ *)
+ let empty_node = (0L, false, false) in
+ let pcpus = Array.make_matrix nr_pcpus nr_doms empty_node in
+
+ List.iteri (
+ fun di (domid, name, nr_vcpus, vcpu_infos, prev_vcpu_infos,
+ cpumaps, maplen) ->
+ (* Which pCPUs can this dom run on? *)
+ for v = 0 to nr_vcpus-1 do
+ let pcpu = vcpu_infos.(v).D.cpu in (* instantaneous pCPU *)
+ let nr_poss_pcpus = ref 0 in (* how many pcpus can it run on? *)
+ for p = 0 to nr_pcpus-1 do
+ (* vcpu v can reside on pcpu p *)
+ if C.cpu_usable cpumaps maplen v p then
+ incr nr_poss_pcpus
+ done;
+ let nr_poss_pcpus = Int64.of_int !nr_poss_pcpus in
+ for p = 0 to nr_pcpus-1 do
+ (* vcpu v can reside on pcpu p *)
+ if C.cpu_usable cpumaps maplen v p then
+ let vcpu_time_on_pcpu =
+ vcpu_infos.(v).D.vcpu_time
+ -^ prev_vcpu_infos.(v).D.vcpu_time in
+ let vcpu_time_on_pcpu =
+ vcpu_time_on_pcpu /^ nr_poss_pcpus in
+ pcpus.(p).(di) <-
+ (vcpu_time_on_pcpu, nr_poss_pcpus > 1L, p = pcpu)
+ done
+ done
+ ) doms;
+
+ (* Sum the CPU time used by each pCPU, for the %CPU column. *)
+ let pcpus_cpu_time = Array.map (
+ fun row ->
+ let cpu_time = ref 0L in
+ for di = 0 to Array.length row-1 do
+ let t, _, _ = row.(di) in
+ cpu_time := !cpu_time +^ t
+ done;
+ Int64.to_float !cpu_time
+ ) pcpus in
+
+ Some (doms, pcpus, pcpus_cpu_time)
+ ) else
+ None in
+
+ (* Calculate totals. *)
+ let totals = List.fold_left (
+ fun (count, running, blocked, paused, shutdown, shutoff,
+ crashed, active, inactive,
+ total_cpu_time, total_memory, total_domU_memory) ->
+ function
+ | (name, Active rd) ->
+ let test state orig =
+ if rd.rd_info.D.state = state then orig+1 else orig
+ in
+ let running = test D.InfoRunning running in
+ let blocked = test D.InfoBlocked blocked in
+ let paused = test D.InfoPaused paused in
+ let shutdown = test D.InfoShutdown shutdown in
+ let shutoff = test D.InfoShutoff shutoff in
+ let crashed = test D.InfoCrashed crashed in
+
+ let total_cpu_time = total_cpu_time +. rd.rd_cpu_time in
+ let total_memory = total_memory +^ rd.rd_info.D.memory in
+ let total_domU_memory = total_domU_memory +^
+ if rd.rd_domid > 0 then rd.rd_info.D.memory else 0L in
+
+ (count+1, running, blocked, paused, shutdown, shutoff,
+ crashed, active+1, inactive,
+ total_cpu_time, total_memory, total_domU_memory)
+
+ | (name, Inactive) -> (* inactive domain *)
+ (count+1, running, blocked, paused, shutdown, shutoff,
+ crashed, active, inactive+1,
+ total_cpu_time, total_memory, total_domU_memory)
+ ) (0,0,0,0,0,0,0,0,0, 0.,0L,0L) doms in
+
+ (* Update last_time, last_info. *)
+ last_time := time;
+ Hashtbl.clear last_info;
+ List.iter (
+ function
+ | (_, Active rd) ->
+ let info = rd.rd_info, rd.rd_block_stats, rd.rd_interface_stats in
+ Hashtbl.add last_info rd.rd_domid info
+ | _ -> ()
+ ) doms;
+
+ (doms,
+ time, printable_time,
+ nr_pcpus, total_cpu, total_cpu_per_pcpu,
+ totals,
+ pcpu_display)
+ in
+
+ collect, clear_pcpu_display_data
+
+(* Redraw the display. *)
+let redraw =
+ (* Keep a historical list of %CPU usages. *)
+ let historical_cpu = ref [] in
+ let historical_cpu_last_time = ref (Unix.gettimeofday ()) in
+ fun
+ (_, _, _, _, node_info, _, _) (* setup *)
+ (doms,
+ time, printable_time,
+ nr_pcpus, total_cpu, total_cpu_per_pcpu,
+ totals,
+ pcpu_display) (* state *) ->
+ clear ();
+
+ (* Get the screen/window size. *)
+ let lines, cols = get_size () in
+
+ (* Time. *)
+ mvaddstr top_lineno 0 (sprintf "virt-top %s - " printable_time);
+
+ (* Basic node_info. *)
+ addstr
+ (sprintf "%s %d/%dCPU %dMHz %LdMB "
+ node_info.C.model node_info.C.cpus nr_pcpus node_info.C.mhz
+ (node_info.C.memory /^ 1024L));
+ (* Save the cursor position for when we come to draw the
+ * historical CPU times (down in this function).
+ *)
+ let stdscr = stdscr () in
+ let historical_cursor = getyx stdscr in
+