Updated kernel db.
[virt-mem.git] / lib / virt_mem.ml
index 69a0821..fd99b86 100644 (file)
@@ -42,12 +42,20 @@ let kernel_size =
 let tools = ref []
 
 (* Registration function used by the tools. *)
-let register ?(external_cmd = true) ?(extra_args = [])
-    ?argcheck ?beforeksyms ?beforeutsname ?run
+let register
+    ?(needs_ksyms = false) ?(needs_utsname = false)
+    ?(needs_tasks = false) ?(needs_net_devices = false)
+    ?(needs_everything = false)
+    ~run
+    ?(external_cmd = true)
+    ?(extra_args = [])
+    ?argcheck
     name summary description =
   tools :=
-    (name, (name, summary, description, external_cmd, extra_args,
-           argcheck, beforeksyms, beforeutsname, run))
+    (name, (name, summary, description,
+           needs_ksyms, needs_utsname, needs_tasks, needs_net_devices,
+           needs_everything,
+           run, external_cmd, extra_args, argcheck))
   :: !tools
 
 (* Main program, called from mem/virt_mem_main.ml when all the
@@ -105,7 +113,7 @@ let main () =
     match tool with
     | None ->                          (* Generic usage message. *)
        let tools = List.map (
-         fun (name, (_, summary, _, external_cmd, _, _, _, _, _)) ->
+         fun (name, (_, summary, _, _, _, _, _, _, _, external_cmd, _, _)) ->
            if external_cmd then "virt-"^name, summary
            else                 "virt-mem "^name, summary
        ) tools in
@@ -127,12 +135,12 @@ General usage is:
   <tool> [-options] [domains...]
 
 To display extra help for a single tool, do:
-  virt-mem help <tool>
+  virt-mem --help <tool>
 
 Options:") tools
 
                                         (* Tool-specific usage message. *)
-    | Some (name, summary, description, external_cmd, _, _, _, _, _) ->
+    | Some (name, summary, description, _, _, _, _, _, _, external_cmd, _, _) ->
        let cmd =
          if external_cmd then "virt-" ^ name else "virt-mem " ^ name in
 
@@ -233,6 +241,12 @@ Options:") cmd summary description in
     exit 0
   in
 
+  (* Handle --list-kernels option. *)
+  let list_kernels () =
+    List.iter print_endline Virt_mem_kernels.kernels;
+    exit 0
+  in
+
   (* Function to collect up any anonymous args (domain names/IDs). *)
   let anon_arg str = anon_args := str :: !anon_args in
 
@@ -242,7 +256,7 @@ Options:") cmd summary description in
   let argspec =
     let extra_args = match tool with
       | None -> []
-      | Some (_, _, _, _, extra_args, _, _, _, _) -> extra_args in
+      | Some (_, _,_, _, _, _, _, _, _, _, extra_args, _) -> extra_args in
     let argspec = [
       "-A", Arg.String set_architecture,
         "arch " ^ s_"Set kernel architecture, endianness and word size";
@@ -258,6 +272,8 @@ Options:") cmd summary description in
         "uri " ^ s_ "Connect to URI";
       "--debug", Arg.Set debug,
         " " ^ s_"Debug mode (default: false)";
+      "--list-kernels", Arg.Unit list_kernels,
+        " " ^ s_"List known kernels";
       "-t", Arg.String memory_image,
         "image " ^ s_"Use saved kernel memory image";
       "--version", Arg.Unit version,
@@ -293,7 +309,10 @@ Options:") cmd summary description in
    * or the user didn't give us a valid tool (eg. "virt-mem foobar").
    * Detect that final case now and give an error.
    *)
-  let name, _, _, _, _, argcheck, beforeksyms,  beforeutsname, run =
+  let name, _, _,
+    needs_ksyms, needs_utsname, needs_tasks, needs_net_devices,
+    needs_everything,
+    run, external_cmd, extra_args, argcheck =
     match tool with
     | Some t -> t
     | None ->
@@ -311,7 +330,7 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)");
   );
 
   (* Get the kernel images. *)
-  let images =
+  let kimages =
     if testimages = [] then (
       let conn =
        let name = uri in
@@ -406,7 +425,7 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)");
          (* Download the static part of the kernel. *)
          let start_t = gettimeofday () in
 
-         let image =
+         let kimage =
            try
              load_static_memory ~dom ~domname ~arch
                ~wordsize ~endian
@@ -425,7 +444,7 @@ Possibly the '-T' command line parameter was used inconsistently.");
              (end_t -. start_t)
          );
 
-         image
+         kimage
 
       ) xmls
     ) else (
@@ -475,73 +494,153 @@ Possibly the '-T' command line parameter was used inconsistently.");
          let mem = Virt_mem_mmap.set_wordsize mem wordsize in
          let mem = Virt_mem_mmap.set_endian mem endian in
 
-         { dom = None; domname = filename; mem = mem; arch = arch;
-           kernel_min = kernel_min; kernel_max = kernel_max }
+         { dom = None; domname = filename; arch = arch;
+           kernel_min = kernel_min; kernel_max = kernel_max;
+           mem = mem; addrmap = Kernel.AddrMap.empty;
+           ksyms = Ksymmap.empty; have_ksyms = false; have_kallsyms = false;
+           utsname = None;
+           have_tasks = false; have_net_devices = false }
       ) testimages
     ) in
 
-  (* Optional callback into the tool before we start looking for
-   * kernel symbols.
-   *)
-  (match beforeksyms with
-   | None -> ()
-   | Some beforeksyms -> beforeksyms debug images
-  );
-
-  (* If there are no more callback functions, then there is no point
-   * continuing with the rest of the program (kernel symbol analysis) ...
-   *)
-  if beforeutsname = None && run = None then exit 0;
-
-  (* Do the kernel symbol analysis. *)
-  let images =
-    List.map (
-      fun image ->
-       (* Look for ordinary kernel symbols: *)
-       let image = Virt_mem_ksyms.find_kernel_symbols debug image in
-       (* Look for kallsyms: *)
-       let image = Virt_mem_kallsyms.find_kallsyms debug image in
-
-(*
-       (* Finally, just wrap the lookup_ksym call in something
-        * which prints the query when debug is set.
-        *)
-       let image =
-         if debug then
-           let (domid, name, arch, mem, lookup_ksym) = image in
-           let lookup_ksym sym =
-             try
-               let value = lookup_ksym sym in
-               eprintf "lookup_ksym %S = %Lx\n%!" sym value;
-               value
-             with Not_found ->
-               eprintf "lookup_ksym %S failed\n%!" sym;
-               raise Not_found
-           in
-           (domid, name, arch, mem, lookup_ksym)
-         else
-           image in
-*)
-
-       image
-    ) images in
-
-  (* Before utsname analysis. *)
-  (match beforeutsname with
-   | None -> ()
-   | Some beforeutsname -> List.iter (beforeutsname debug) images
-  );
-
-  (* If there are no more callback functions, then there is no point
-   * continuing with the rest of the program (kernel version analysis) ...
-   *)
-  if run = None then exit 0;
-
-  (* Get the kernel version (utsname analysis). *)
-  let images = List.map (Virt_mem_utsname.find_utsname debug) images in
-
-  (* Run the tool's main function. *)
-  (match run with
-   | None -> ()
-   | Some run -> List.iter (run debug) images
-  )
+  (* Certain needs are dependent on others ... *)
+  let needs_ksyms =
+    if needs_utsname then true
+    else needs_ksyms in
+  let needs_ksyms, needs_utsname =
+    if needs_tasks then true, true
+    else needs_ksyms, needs_utsname in
+  let needs_ksyms, needs_utsname =
+    if needs_net_devices then true, true
+    else needs_ksyms, needs_utsname in
+  let needs_ksyms, needs_utsname, needs_tasks, needs_net_devices =
+    if needs_everything then true, true, true, true
+    else needs_ksyms, needs_utsname, needs_tasks, needs_net_devices in
+
+  let errors = ref 0 in
+
+  List.iter (
+    fun kimage ->
+      try
+       (* Do the kernel symbol analysis. *)
+       let kimage =
+         if not needs_ksyms then kimage
+         else (
+           (* Look for ordinary kernel symbols: *)
+           let kimage = Virt_mem_ksyms.find_kernel_symbols debug kimage in
+
+           if kimage.have_ksyms then
+             (* Look for kallsyms: *)
+             Virt_mem_kallsyms.find_kallsyms debug kimage
+           else kimage
+         ) in
+
+       (* Get the kernel version (utsname analysis). *)
+       let kimage =
+         if not needs_utsname then kimage
+         else (
+           if kimage.have_ksyms then
+             Virt_mem_utsname.find_utsname debug kimage
+           else
+             kimage
+         ) in
+
+       (* Get the tasks. *)
+       let kimage =
+         if not needs_tasks then kimage
+         else (
+           let { ksyms = ksyms; have_ksyms = have_ksyms; utsname = utsname;
+                 addrmap = addrmap } = kimage in
+           match have_ksyms, utsname with
+           | true, Some { uts_kernel_release = kernel_version } ->
+               let kimage = ref kimage in
+               let load struct_name addr size =
+                 if debug then
+                   eprintf "load for task, %s: %Lx %d ...\n"
+                     struct_name addr size;
+                 let mapped =
+                   Virt_mem_mmap.is_mapped_range !kimage.mem addr size in
+                 if not mapped then kimage := load_memory !kimage addr size;
+                 let bits = Virt_mem_mmap.get_bytes !kimage.mem addr size in
+                 Bitstring.bitstring_of_string bits
+               in
+               let addrmap =
+                 try
+                   let init_task = Ksymmap.find "init_task" ksyms in
+                   Some (Kernel.task_struct_follower debug kernel_version
+                           load addrmap init_task)
+                 with
+                   Not_found ->
+                     eprintf (f_"%s: cannot find init_task symbol in kernel image.\n") !kimage.domname;
+                     None in
+               (match addrmap with
+                | None -> !kimage
+                | Some addrmap ->
+                    { !kimage with addrmap = addrmap; have_tasks = true }
+               )
+           | _, _ -> kimage
+         ) in
+
+       (* Get the net devices. *)
+       let kimage =
+         if not needs_net_devices then kimage
+         else (
+           let { ksyms = ksyms; have_ksyms = have_ksyms; utsname = utsname;
+                 addrmap = addrmap } = kimage in
+           match have_ksyms, utsname with
+           | true, Some { uts_kernel_release = kernel_version } ->
+               let kimage = ref kimage in
+               let load struct_name addr size =
+                 if debug then
+                   eprintf "load for net dev, %s: %Lx %d ...\n"
+                     struct_name addr size;
+                 let mapped =
+                   Virt_mem_mmap.is_mapped_range !kimage.mem addr size in
+                 if not mapped then kimage := load_memory !kimage addr size;
+                 let bits = Virt_mem_mmap.get_bytes !kimage.mem addr size in
+                 Bitstring.bitstring_of_string bits
+               in
+               let addrmap =
+                 try
+                   let dev_base = Ksymmap.find "dev_base" ksyms in
+                   Some (Kernel.net_device_follower debug kernel_version
+                           load addrmap dev_base)
+                 with Not_found ->
+                   try
+                     let dev_base_head = Ksymmap.find "dev_base_head" ksyms in
+                     (* XXX adjust to get offset of start of net_device *)
+                     assert false
+                   with Not_found ->
+                     try
+                       let init_net = Ksymmap.find "init_net" ksyms in
+                       Some (Kernel.net_follower debug kernel_version
+                               load addrmap init_net)
+                     with Not_found ->
+                       eprintf (f_"%s: cannot find dev_base, dev_base_head or init_net symbols in kernel image.\n") !kimage.domname;
+                       None in
+               (match addrmap with
+                | None -> !kimage
+                | Some addrmap ->
+                    { !kimage with addrmap = addrmap; have_net_devices = true }
+               )
+           | _, _ -> kimage
+         ) in
+
+       (* Run the tool's main function. *)
+       if not needs_everything then (
+         if needs_ksyms && kimage.have_ksyms = false then
+           failwith (s_"could not read kernel symbols")
+         else if needs_utsname && kimage.utsname = None then
+           failwith (s_"could not read kernel version")
+         else if needs_tasks && kimage.have_tasks = false then
+           failwith (s_"could not read process table")
+         else if needs_net_devices && kimage.have_net_devices = false then
+           failwith (s_"could not read net device table")
+       );
+       run debug kimage
+      with exn ->
+       eprintf "%s: %s\n" kimage.domname (Printexc.to_string exn);
+       incr errors
+  ) kimages;
+
+  exit (if !errors > 0 then 1 else 0)