diff --git a/_tags b/_tags index 83d849c..e620759 100644 --- a/_tags +++ b/_tags @@ -15,6 +15,7 @@ true : package(rresult logs ipaddr bos hex ptime astring duration cstruct decomp : package(ptime.clock.os) : package(tls.lwt ptime.clock.os) : link_vmm_stats +: link_vmm_stats : package(nocrypto tls.lwt nocrypto.lwt) : package(nocrypto tls.lwt nocrypto.lwt) diff --git a/app/vmm_stats_pure.ml b/app/vmm_stats_pure.ml index 1111aff..06a62fa 100644 --- a/app/vmm_stats_pure.ml +++ b/app/vmm_stats_pure.ml @@ -6,6 +6,7 @@ open Rresult.R.Infix open Vmm_core external sysctl_rusage : int -> Stats.rusage = "vmmanage_sysctl_rusage" +external sysctl_kinfo_mem : int -> Stats.kinfo_mem = "vmmanage_sysctl_kinfo_mem" external sysctl_ifcount : unit -> int = "vmmanage_sysctl_ifcount" external sysctl_ifdata : int -> Stats.ifdata = "vmmanage_sysctl_ifdata" @@ -83,6 +84,7 @@ let try_open_vmmapi pid_nic = let gather pid vmctx nics = wrap sysctl_rusage pid, + wrap sysctl_kinfo_mem pid, (match vmctx with | Error _ -> None | Ok vmctx -> wrap vmmapi_stats vmctx), @@ -105,13 +107,13 @@ let tick t = | xs -> match IM.find_opt pid t.pid_nic with | None -> Logs.warn (fun m -> m "couldn't find nics of %d" pid) ; out | Some (vmctx, nics) -> - let ru, vmm, ifd = gather pid vmctx nics in + let ru, mem, vmm, ifd = gather pid vmctx nics in match ru with | None -> Logs.err (fun m -> m "failed to get rusage for %d" pid) ; out | Some ru' -> let stats = let vmm' = match vmm with None -> None | Some xs -> Some (List.combine !descr xs) in - ru', vmm', ifd + ru', mem, vmm', ifd in List.fold_left (fun out (id, socket) -> match Vmm_core.Name.drop_super ~super:id ~sub:vmid with diff --git a/app/vmm_stats_stubs.c b/app/vmm_stats_stubs.c index 2773bda..d31ff4d 100644 --- a/app/vmm_stats_stubs.c +++ b/app/vmm_stats_stubs.c @@ -21,6 +21,34 @@ #include #include +CAMLprim value vmmanage_sysctl_kinfo_mem (value pid_r) { + CAMLparam1(pid_r); + CAMLlocal3(res, utime, stime); + int name[4]; + int error; + size_t len; + struct kinfo_proc p; + + len = sizeof(p); + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PID; + name[3] = Int_val(pid_r); + + error = sysctl(name, nitems(name), &p, &len, NULL, 0); + if (error < 0) + uerror("sysctl", Nothing); + + res = caml_alloc(5, 0); + Store_field (res, 0, Val64(p.ki_size)); + Store_field (res, 1, Val64(p.ki_rssize)); + Store_field (res, 2, Val64(p.ki_tsize)); + Store_field (res, 3, Val64(p.ki_dsize)); + Store_field (res, 4, Val64(p.ki_ssize)); + + CAMLreturn(res); +} + CAMLprim value vmmanage_sysctl_rusage (value pid_r) { CAMLparam1(pid_r); CAMLlocal3(res, utime, stime); @@ -201,6 +229,11 @@ CAMLprim value vmmanage_sysctl_rusage (value pid_r) { uerror("sysctl_rusage", Nothing); } +CAMLprim value vmmanage_sysctl_kinfo_mem (value pid_r) { + CAMLparam1(pid_r); + uerror("sysctl_kinfo_mem", Nothing); +} + CAMLprim value vmmanage_sysctl_ifcount (value unit) { CAMLparam1(unit); uerror("sysctl_ifcount", Nothing); diff --git a/app/vmmc_stat.ml b/app/vmmc_stat.ml new file mode 100644 index 0000000..e3c2ddf --- /dev/null +++ b/app/vmmc_stat.ml @@ -0,0 +1,57 @@ +open Vmm_core +open Vmm_stats_pure + +let timer pid vmmapi = + let rusage = sysctl_rusage pid in + Logs.app (fun m -> m "sysctl rusage: %a" Stats.pp_rusage_mem rusage) ; + let kinfo_mem = sysctl_kinfo_mem pid in + Logs.app (fun m -> m "kinfo mem: %a" Stats.pp_kinfo_mem kinfo_mem) ; + match vmmapi with + | None -> () + | Some vmctx -> match wrap vmmapi_stats vmctx with + | None -> Logs.app (fun m -> m "no vmctx stats") + | Some st -> + let all = List.combine !descr st in + Logs.app (fun m -> m "bhyve stats %a" Stats.pp_vmm_mem all) + +let jump _ pid name interval = + Sys.(set_signal sigpipe Signal_ignore) ; + let interval = Duration.(to_f (of_sec interval)) in + Lwt_main.run ( + let vmmapi = match name with + | None -> + Logs.warn (fun m -> m "no name, no vmmapi") ; + None + | Some name -> match wrap vmmapi_open name with + | None -> + Logs.warn (fun m -> m "vmmapi_open failed for %s" name) ; + None + | Some vmctx -> + Logs.info (fun m -> m "vmmapi_open succeeded for %s" name) ; + Vmm_stats_pure.fill_descr vmctx ; + Some vmctx + in + let _ev = Lwt_engine.on_timer interval true (fun _e -> timer pid vmmapi) in + let t, _u = Lwt.task () in + t) + +open Cmdliner +open Vmm_cli + +let interval = + let doc = "Interval between statistics gatherings (in seconds)" in + Arg.(value & opt int 10 & info [ "interval" ] ~doc) + +let pid = + let doc = "Process id (defaults to own pid)" in + Arg.(value & opt int (Unix.getpid ()) & info [ "pid" ] ~doc) + +let vmname = + let doc = "VM name" in + Arg.(value & opt (some string) None & info [ "name" ] ~doc) + +let cmd = + Term.(ret (const jump $ setup_log $ pid $ vmname $ interval)), + Term.info "vmmd_stats" ~version:"%%VERSION_NUM%%" + +let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 diff --git a/app/vmmd_influx.ml b/app/vmmd_influx.ml index a13b3e9..41925de 100644 --- a/app/vmmd_influx.ml +++ b/app/vmmd_influx.ml @@ -100,6 +100,18 @@ module P = struct let fields = List.map (fun (k, v) -> k ^ "=" ^ v) fields in Printf.sprintf "resource_usage,vm=%s %s" vm (String.concat ~sep:"," fields) + let encode_kinfo_mem vm mem = + let fields = + [ "vsize", i64 mem.vsize ; + "rss", i64 mem.rss ; + "rsize", i64 mem.tsize ; + "dsize", i64 mem.dsize ; + "ssize", i64 mem.ssize ; + ] + in + let fields = List.map (fun (k, v) -> k ^ "=" ^ v) fields in + Printf.sprintf "kinfo_mem,vm=%s %s" vm (String.concat ~sep:"," fields) + let encode_vmm vm xs = let escape s = let cutted = String.cuts ~sep:"," s in @@ -190,7 +202,7 @@ let rec read_sock_write_tcp c ?fd addr addrtype = safe_close fd >>= fun () -> safe_close c >|= fun () -> true - | Ok (hdr, `Data (`Stats_data (ru, vmm, ifs))) -> + | Ok (hdr, `Data (`Stats_data (ru, mem, vmm, ifs))) -> begin if not (Vmm_commands.version_eq hdr.Vmm_commands.version my_version) then begin Logs.err (fun m -> m "unknown wire protocol version") ; @@ -200,9 +212,10 @@ let rec read_sock_write_tcp c ?fd addr addrtype = end else let name = Name.to_string hdr.Vmm_commands.name in let ru = P.encode_ru name ru in - let vmm = match vmm with None -> [] | Some xs -> [ P.encode_vmm name xs ] in + let mem = match mem with None -> [] | Some m -> [ P.encode_kinfo_mem name m ] in + let vmm = match vmm with None -> [] | Some vmm -> [ P.encode_vmm name vmm ] in let taps = List.map (P.encode_if name) ifs in - let out = (String.concat ~sep:"\n" (ru :: vmm @ taps)) ^ "\n" in + let out = (String.concat ~sep:"\n" (ru :: mem @ vmm @ taps)) ^ "\n" in Logs.debug (fun m -> m "writing %d via tcp" (String.length out)) ; Vmm_lwt.write_raw fd (Bytes.unsafe_of_string out) >>= function | Ok () -> diff --git a/pkg/pkg.ml b/pkg/pkg.ml index 1a80c8b..b8db123 100644 --- a/pkg/pkg.ml +++ b/pkg/pkg.ml @@ -18,4 +18,5 @@ let () = Pkg.bin "app/vmmc_bistro" ; Pkg.bin "app/vmmp_request" ; Pkg.bin "app/vmmp_ca" ; + Pkg.test ~run:false "app/vmmc_stat" ; ] diff --git a/src/vmm_asn.ml b/src/vmm_asn.ml index c8522d6..640846b 100644 --- a/src/vmm_asn.ml +++ b/src/vmm_asn.ml @@ -120,6 +120,21 @@ let ru = @ (required ~label:"nvcsw" int64) -@ (required ~label:"nivcsw" int64)) +let kinfo_mem = + let open Stats in + let f (vsize, rss, tsize, dsize, ssize) = + { vsize ; rss ; tsize ; dsize ; ssize } + and g t = + (t.vsize, t.rss, t.tsize, t.dsize, t.ssize) + in + Asn.S.map f g @@ + Asn.S.(sequence5 + (required ~label:"bsize" int64) + (required ~label:"rss" int64) + (required ~label:"tsize" int64) + (required ~label:"dsize" int64) + (required ~label:"ssize" int64)) + (* TODO is this good? *) let int32 = let f i = Int32.of_int i @@ -349,11 +364,11 @@ let wire_command = let data = let f = function | `C1 (timestamp, data) -> `Console_data (timestamp, data) - | `C2 (ru, ifs, vmm) -> `Stats_data (ru, vmm, ifs) + | `C2 (ru, ifs, vmm, mem) -> `Stats_data (ru, mem, vmm, ifs) | `C3 (timestamp, event) -> `Log_data (timestamp, event) and g = function | `Console_data (timestamp, data) -> `C1 (timestamp, data) - | `Stats_data (ru, ifs, vmm) -> `C2 (ru, vmm, ifs) + | `Stats_data (ru, mem, ifs, vmm) -> `C2 (ru, vmm, ifs, mem) | `Log_data (timestamp, event) -> `C3 (timestamp, event) in Asn.S.map f g @@ @@ -361,13 +376,14 @@ let data = (explicit 0 (sequence2 (required ~label:"timestamp" utc_time) (required ~label:"data" utf8_string))) - (explicit 1 (sequence3 + (explicit 1 (sequence4 (required ~label:"resource_usage" ru) (required ~label:"ifdata" (sequence_of ifdata)) - (optional ~label:"vmm_stats" + (optional ~label:"vmm_stats" @@ explicit 0 (sequence_of (sequence2 (required ~label:"key" utf8_string) - (required ~label:"value" int64)))))) + (required ~label:"value" int64)))) + (optional ~label:"kinfo_mem" @@ implicit 1 kinfo_mem))) (explicit 2 (sequence2 (required ~label:"timestamp" utc_time) (required ~label:"event" log_event)))) diff --git a/src/vmm_core.ml b/src/vmm_core.ml index ac86641..035e214 100644 --- a/src/vmm_core.ml +++ b/src/vmm_core.ml @@ -213,11 +213,28 @@ module Stats = struct let pp_rusage ppf r = Fmt.pf ppf "utime %Lu.%d stime %Lu.%d maxrss %Lu ixrss %Lu idrss %Lu isrss %Lu minflt %Lu majflt %Lu nswap %Lu inblock %Lu outblock %Lu msgsnd %Lu msgrcv %Lu signals %Lu nvcsw %Lu nivcsw %Lu" (fst r.utime) (snd r.utime) (fst r.stime) (snd r.stime) r.maxrss r.ixrss r.idrss r.isrss r.minflt r.majflt r.nswap r.inblock r.outblock r.msgsnd r.msgrcv r.nsignals r.nvcsw r.nivcsw + let pp_rusage_mem ppf r = + Fmt.pf ppf "maxrss %Lu ixrss %Lu idrss %Lu isrss %Lu minflt %Lu majflt %Lu" + r.maxrss r.ixrss r.idrss r.isrss r.minflt r.majflt + type kinfo_mem = { + vsize : int64 ; + rss : int64 ; + tsize : int64 ; + dsize : int64 ; + ssize : int64 ; + } + + let pp_kinfo_mem ppf t = + Fmt.pf ppf "virtual-size %Lu rss %Lu text-size %Lu data-size %Lu stack-size %Lu" + t.vsize t.rss t.tsize t.dsize t.ssize type vmm = (string * int64) list let pp_vmm ppf vmm = Fmt.(list ~sep:(unit "@.") (pair ~sep:(unit ": ") string int64)) ppf vmm + let pp_vmm_mem ppf vmm = + Fmt.(list ~sep:(unit "@.") (pair ~sep:(unit ": ") string int64)) ppf + (List.filter (fun (k, _) -> k = "Resident memory" || k = "Wired memory") vmm) type ifdata = { ifname : string ; @@ -244,10 +261,11 @@ module Stats = struct Fmt.pf ppf "ifname %s flags %lX send_length %lu max_send_length %lu send_drops %lu mtu %lu baudrate %Lu input_packets %Lu input_errors %Lu output_packets %Lu output_errors %Lu collisions %Lu input_bytes %Lu output_bytes %Lu input_mcast %Lu output_mcast %Lu input_dropped %Lu output_dropped %Lu" i.ifname i.flags i.send_length i.max_send_length i.send_drops i.mtu i.baudrate i.input_packets i.input_errors i.output_packets i.output_errors i.collisions i.input_bytes i.output_bytes i.input_mcast i.output_mcast i.input_dropped i.output_dropped - type t = rusage * vmm option * ifdata list - let pp ppf (ru, vmm, ifs) = - Fmt.pf ppf "%a@.%a@.%a" + type t = rusage * kinfo_mem option * vmm option * ifdata list + let pp ppf (ru, mem, vmm, ifs) = + Fmt.pf ppf "%a@.%a@.%a@.%a" pp_rusage ru + Fmt.(option ~none:(unit "no kinfo_mem stats") pp_kinfo_mem) mem Fmt.(option ~none:(unit "no vmm stats") pp_vmm) vmm Fmt.(list ~sep:(unit "@.@.") pp_ifdata) ifs end @@ -279,8 +297,8 @@ module Log = struct let pp_log_event ppf = function | `Startup -> Fmt.string ppf "startup" - | `Login (name, ip, port) -> Fmt.pf ppf "%a login %a:%d" Name.pp name Ipaddr.V4.pp_hum ip port - | `Logout (name, ip, port) -> Fmt.pf ppf "%a logout %a:%d" Name.pp name Ipaddr.V4.pp_hum ip port + | `Login (name, ip, port) -> Fmt.pf ppf "%a login %a:%d" Name.pp name Ipaddr.V4.pp ip port + | `Logout (name, ip, port) -> Fmt.pf ppf "%a logout %a:%d" Name.pp name Ipaddr.V4.pp ip port | `Unikernel_start (name, pid, taps, block) -> Fmt.pf ppf "%a started %d (tap %a, block %a)" Name.pp name pid Fmt.(list ~sep:(unit "; ") string) taps diff --git a/src/vmm_core.mli b/src/vmm_core.mli index 7a18b9f..8f47abc 100644 --- a/src/vmm_core.mli +++ b/src/vmm_core.mli @@ -101,9 +101,21 @@ module Stats : sig nivcsw : int64; } val pp_rusage : rusage Fmt.t + val pp_rusage_mem : rusage Fmt.t + + type kinfo_mem = { + vsize : int64 ; + rss : int64 ; + tsize : int64 ; + dsize : int64 ; + ssize : int64 ; + } + + val pp_kinfo_mem : kinfo_mem Fmt.t type vmm = (string * int64) list val pp_vmm : vmm Fmt.t + val pp_vmm_mem : vmm Fmt.t type ifdata = { ifname : string; @@ -127,7 +139,7 @@ module Stats : sig } val pp_ifdata : ifdata Fmt.t - type t = rusage * vmm option * ifdata list + type t = rusage * kinfo_mem option * vmm option * ifdata list val pp : t Fmt.t end