New kernel database parser *NOT WORKING YET*.
[virt-mem.git] / extract / codegen / compile_kerneldb.ml
diff --git a/extract/codegen/compile_kerneldb.ml b/extract/codegen/compile_kerneldb.ml
new file mode 100644 (file)
index 0000000..27fcb14
--- /dev/null
@@ -0,0 +1,382 @@
+(* Memory info for virtual domains.
+   (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
+   http://libvirt.org/
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*)
+
+(* This program takes the kernel database (in kernels/ in toplevel
+   directory) and generates parsing code for the various structures
+   in the kernel that we are interested in.
+
+   The output programs -- *.ml, *.mli files of generated code -- go
+   into lib/ at the toplevel, eg. lib/virt_mem_kernels.ml
+
+   The stuff at the top of this file determine what structures
+   we try to parse.
+*)
+
+type struct_t = {
+  good_fields : string list;
+  field_metadata : (string * field_metadata_t) list;
+}
+and field_metadata_t =
+  | VoidPointerIsReally of string
+  | ListHeadIsReally of string
+
+(*----------------------------------------------------------------------
+ * This controls what structures & fields we will parse out.
+ *----------------------------------------------------------------------*)
+let good_structs = [
+  "task_struct", {
+    good_fields = [ "tasks'next"; "tasks'prev";
+                   "run_list'next"; "run_list'prev";
+                   "state"; "prio"; "static_prio"; "normal_prio";
+                   "comm"; "pid" ];
+    field_metadata = [
+      "tasks'next", ListHeadIsReally "task_struct";
+      "tasks'prev", ListHeadIsReally "task_struct";
+      "run_list'next", ListHeadIsReally "task_struct";
+      "run_list'prev", ListHeadIsReally "task_struct";
+    ];
+  };
+  "net_device", {
+    good_fields = [ "dev_list'prev"; "dev_list'next"; "next";
+                   "ip_ptr"; "ip6_ptr";
+                   "name"; "flags"; "operstate"; "mtu"; "perm_addr";
+                   "addr_len" ];
+    field_metadata = [
+      "dev_list'next", ListHeadIsReally "net_device";
+      "dev_list'prev", ListHeadIsReally "net_device";
+      "ip_ptr", VoidPointerIsReally "in_device";
+      "ip6_ptr", VoidPointerIsReally "inet6_dev";
+    ];
+  };
+  "net", {
+    good_fields = [ "dev_base_head'next"; "dev_base_head'prev" ];
+    field_metadata = [
+      "dev_base_head'next", ListHeadIsReally "net_device";
+      "dev_base_head'prev", ListHeadIsReally "net_device";
+    ];
+  };
+  "in_device", {
+    good_fields = [ "ifa_list" ];
+    field_metadata = [];
+  };
+  "inet6_dev", {
+    good_fields = [ "addr_list" ];
+    field_metadata = [];
+  };
+  "in_ifaddr", {
+    good_fields = [ "ifa_next"; "ifa_local"; "ifa_address";
+                   "ifa_mask"; "ifa_broadcast" ];
+    field_metadata = [];
+  };
+  "inet6_ifaddr", {
+    good_fields = [ "prefix_len"; "lst_next" ];
+    field_metadata = [];
+  };
+]
+
+let debug = true
+
+open Camlp4.PreCast
+open Syntax
+(*open Ast*)
+
+open ExtList
+open ExtString
+open Printf
+
+module PP = Pahole_parser
+module SC = Struct_classify
+
+let (//) = Filename.concat
+
+(* Couple of handy camlp4 construction functions which do some
+ * things that ought to be easy/obvious but aren't.
+ *
+ * 'concat_str_items' concatenates a list of str_item together into
+ * one big str_item.
+ *
+ * 'concat_record_fields' concatenates a list of records fields into
+ * a record.  The list must have at least one element.
+ *
+ * 'build_record' builds a record out of record fields.
+ * 
+ * 'build_tuple_from_exprs' builds an arbitrary length tuple from
+ * a list of expressions of length >= 2.
+ *
+ * Thanks to bluestorm on #ocaml for getting these working.
+ *)
+let concat_str_items _loc items =
+  match items with
+  | [] -> <:str_item< >>
+  | x :: xs ->
+      List.fold_left (fun xs x -> <:str_item< $xs$ $x$ >>) x xs
+
+let concat_sig_items _loc items =
+  match items with
+  | [] -> <:sig_item< >>
+  | x :: xs ->
+      List.fold_left (fun xs x -> <:sig_item< $xs$ $x$ >>) x xs
+
+let concat_record_fields _loc fields =
+  match fields with
+    | [] -> assert false
+    | f :: fs ->
+       List.fold_left (fun fs f -> <:ctyp< $fs$ ; $f$ >>) f fs
+
+let concat_record_bindings _loc rbs =
+  match rbs with
+    | [] -> assert false
+    | rb :: rbs ->
+       List.fold_left (fun rbs rb -> <:rec_binding< $rbs$ ; $rb$ >>) rb rbs
+
+let build_record _loc rbs =
+  Ast.ExRec (_loc, rbs, Ast.ExNil _loc)
+
+let build_tuple_from_exprs _loc exprs =
+  match exprs with
+  | [] | [_] -> assert false
+  | x :: xs ->
+      Ast.ExTup (_loc,
+                List.fold_left (fun xs x -> Ast.ExCom (_loc, x, xs)) x xs)
+
+(* Start of the main program. *)
+let () =
+  let quick = ref false in
+  let anon_args = ref [] in
+
+  let argspec = Arg.align [
+    "--quick", Arg.Set quick, " Quick mode (just for testing)";
+  ] in
+
+  let anon_arg str = anon_args := str :: !anon_args in
+  let usage = "\
+compile-kerneldb: Turn kernels database into code modules.
+
+Usage:
+  compile-kerneldb [-options] <kerneldb> <outputdir>
+
+For example, from the top level of the virt-mem source tree:
+  compile-kerneldb kernels/ lib/
+
+Options:
+" in
+  Arg.parse argspec anon_arg usage;
+
+  let quick = !quick in
+  let anon_args = List.rev !anon_args in
+
+  let kernelsdir, outputdir =
+    match anon_args with
+    | [kd;od] -> kd,od
+    | _ ->
+       eprintf "compile-kerneldb <kerneldb> <outputdir>\n";
+       exit 2 in
+
+  (* Read in the list of kernels from the kerneldb. *)
+  let kernels = PP.list_kernels kernelsdir in
+
+  (* In quick mode, just process the first few kernels. *)
+  let kernels = if quick then List.take 10 kernels else kernels in
+
+  let good_struct_names = List.map fst good_structs in
+
+  (* Load in the structures. *)
+  let nr_kernels = List.length kernels in
+  let kernels = List.mapi (
+    fun i info ->
+      printf "Loading kernel data file %d/%d\r%!" (i+1) nr_kernels;
+
+      let structures = PP.load_structures info good_struct_names in
+
+      (info, structures)
+  ) kernels in
+
+  (* Keep only the good fields. *)
+  let kernels = List.map (
+    fun (info, structures) ->
+      let structures = List.map (
+       fun (struct_name, structure) ->
+         let { good_fields = good_fields } =
+           List.assoc struct_name good_structs in
+         let fields = List.filter (
+           fun { PP.field_name = name } -> List.mem name good_fields
+         ) structure.PP.struct_fields in
+         struct_name, { structure with PP.struct_fields = fields }
+      ) structures in
+      (info, structures)
+  ) kernels in
+
+  (* Turn anonymous list_head and void * pointers into pointers to
+   * known structure types, where we have that meta-information.
+   *)
+  let kernels = List.map (
+    fun (info, structures) ->
+      let structures = List.map (
+       fun (struct_name, structure) ->
+         let { field_metadata  = metadata } =
+           List.assoc struct_name good_structs in
+         let fields = structure.PP.struct_fields in
+         let fields = List.map (
+           fun ({ PP.field_name = name; PP.field_type = typ } as field) ->
+             try
+               let meta = List.assoc name metadata in
+               let typ =
+                 match meta, typ with
+                 | ListHeadIsReally s, PP.FAnonListHeadPointer ->
+                     PP.FListHeadPointer s
+                 | VoidPointerIsReally s, PP.FVoidPointer ->
+                     PP.FStructPointer s
+                 | _, typ -> typ in
+               { field with PP.field_type = typ }
+             with
+               Not_found -> field
+         ) fields in
+         struct_name, { structure with PP.struct_fields = fields }
+      ) structures in
+      (info, structures)
+  ) kernels in
+
+  if debug then
+    List.iter (
+      fun (info, structures) ->
+       printf "\n%s ----------\n" (PP.string_of_info info);
+       List.iter (
+         fun (_, structure) ->
+           printf "%s\n\n" (PP.string_of_structure structure);
+       ) structures;
+    ) kernels;
+
+  (* First output file is a simple list of kernels, to support the
+   * 'virt-mem --list-kernels' option.
+   *)
+  let () =
+    let _loc = Loc.ghost in
+
+    let versions = List.map (
+      fun ({ PP.kernel_version = version }, _) -> version
+    ) kernels in
+
+    (* Sort them in reverse because we are going to generate the
+     * final list in reverse.
+     *)
+    let cmp a b = compare b a in
+    let versions = List.sort ~cmp versions in
+
+    let xs =
+      List.fold_left (fun xs version -> <:expr< $str:version$ :: $xs$ >>)
+      <:expr< [] >> versions in
+
+    let code = <:str_item<
+      let kernels = $xs$
+    >> in
+
+    let output_file = outputdir // "virt_mem_kernels.ml" in
+    printf "Writing list of kernels to %s ...\n%!" output_file;
+    Printers.OCaml.print_implem ~output_file code in
+
+  (* We want to track single structures as they have changed over
+   * time, ie. over kernel versions.  Transpose our dataset so we are
+   * looking at structures over time.
+   *)
+  let structures = PP.transpose good_struct_names kernels in
+
+  let kernels = () in ignore (kernels); (* garbage collect *)
+
+  let structures =
+    List.map (
+      fun (struct_name, kernels) ->
+       let all_fields = PP.get_fields kernels in
+       (struct_name, (kernels, all_fields))
+    ) structures in
+
+  if debug then
+    List.iter (
+      fun (struct_name, (kernels, all_fields)) ->
+       printf "struct %s:\n" struct_name;
+       printf "  structure occurs in %d kernel versions\n"
+         (List.length kernels);
+       printf "  union of fields found:\n";
+       List.iter (
+         fun (field_name, field_type) ->
+           printf "    %s %s\n" (PP.string_of_f_type field_type) field_name
+       ) all_fields
+    ) structures;
+
+  (* Now perform the minimization step for each structure.
+   * We do separate minimization for:
+   *   - shape field structures
+   *   - content field structures
+   *   - parsers
+   *)
+  let structures =
+    List.map (
+      fun (struct_name, (kernels, all_fields)) ->
+       let sflist, sfhash =
+         SC.minimize_shape_field_structs struct_name good_struct_names
+           kernels in
+
+       let cflist, cfhash =
+         SC.minimize_content_field_structs struct_name good_struct_names
+           kernels in
+
+       let palist, pahash =
+         SC.minimize_parsers struct_name kernels sfhash cfhash in
+
+       (struct_name, (kernels, all_fields,
+                      sflist, sfhash, cflist, cfhash, palist, pahash))
+    ) structures in
+
+  if debug then
+    List.iter (
+      fun (struct_name,
+          (kernels, all_fields,
+           sflist, sfhash, cflist, cfhash, palist, pahash)) ->
+       printf "struct %s:\n" struct_name;
+
+       printf "  shape field structures:\n";
+       List.iter (
+         fun { SC.sf_name = name; sf_fields = fields } ->
+           printf "    type %s = {\n" name;
+           List.iter (
+             fun { PP.field_name = name; field_type = typ } ->
+               printf "      %s %s;\n" (PP.string_of_f_type typ) name
+           ) fields;
+           printf "    }\n";
+       ) sflist;
+
+       printf "  content field structures:\n";
+       List.iter (
+         fun { SC.cf_name = name; cf_fields = fields } ->
+           printf "    type %s = {\n" name;
+           List.iter (
+             fun { PP.field_name = name; field_type = typ } ->
+               printf "      %s %s;\n" (PP.string_of_f_type typ) name
+           ) fields;
+           printf "    }\n";
+       ) cflist;
+
+       printf "  parsers:\n";
+       List.iter (
+         fun { SC.pa_name = name;
+               pa_shape_field_struct = sf;
+               pa_content_field_struct = cf } ->
+           printf "    let %s = ...\n" name;
+           printf "      -> (%s, %s)\n" sf.SC.sf_name cf.SC.cf_name
+       ) palist
+    ) structures;