Script to generate the parsing code from kernel database.
[virt-mem.git] / extract / codegen / kerneldb_to_parser.ml
diff --git a/extract/codegen/kerneldb_to_parser.ml b/extract/codegen/kerneldb_to_parser.ml
new file mode 100644 (file)
index 0000000..6b64786
--- /dev/null
@@ -0,0 +1,129 @@
+(* 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/kernel_task_struct.ml
+
+   The stuff at the top of this file determine what structures
+   and fields we try to parse.
+*)
+
+let what = [
+  "task_struct", (
+    "struct task_struct",
+    [ "state"; "prio"; "normal_prio"; "static_prio"; "tasks"; "comm"]
+  );
+]
+
+open ExtList
+open ExtString
+open Printf
+
+let (//) = Filename.concat
+
+let () =
+  let args = Array.to_list Sys.argv in
+
+  let kernelsdir, outputdir =
+    match args with
+    | [_;kd;od] -> kd,od
+    | _ ->
+       let arg0 = Filename.basename Sys.executable_name in
+       eprintf "%s - Turn kernels database into code modules.
+
+Usage:
+  %s <kernelsdir> <outputdir>
+
+Example (from toplevel of virt-mem source tree):
+  %s kernels/ lib/
+" arg0 arg0 arg0;
+       exit 2 in
+
+  (* Get the *.info files from the kernels database. *)
+  let infos = Sys.readdir kernelsdir in
+  let infos = Array.to_list infos in
+  let infos = List.filter (fun name -> String.ends_with name ".info") infos in
+  let infos = List.map ((//) kernelsdir) infos in
+
+  (* Regular expressions.  We really really should use ocaml-mikmatch ... *)
+  let re_oldformat = Pcre.regexp "^RPM: \\d+: \\(build \\d+\\) ([-\\w]+) ([\\w.]+) ([\\w.]+) \\(.*?\\) (\\w+)" in
+  let re_keyvalue = Pcre.regexp "^(\\w+): (.*)" in
+
+  (* Parse in the *.info files.  These have historically had a few different
+   * formats that we need to support.
+   *)
+  let infos = List.map (
+    fun filename ->
+      (* Get the basename (for getting the .data file later on). *)
+      let basename = Filename.chop_suffix filename ".info" in
+
+      let chan = open_in filename in
+      let line = input_line chan in
+
+      let name, version =
+       if Pcre.pmatch ~rex:re_oldformat line then (
+         (* If the file starts with "RPM: \d+: ..." then it's the original
+          * format.  Everything in one line.
+          *)
+         let subs = Pcre.exec ~rex:re_oldformat line in
+         let name = Pcre.get_substring subs 1 in
+         let version = Pcre.get_substring subs 2 in
+         let release = Pcre.get_substring subs 3 in
+         let arch = Pcre.get_substring subs 4 in
+         close_in chan;
+         name, sprintf "%s-%s.%s" version release arch
+       ) else (
+         (* New-style "key: value" entries, up to end of file or the first
+          * blank line.
+          *)
+         let name, version, release, arch =
+           ref "", ref "", ref "", ref "" in
+         let rec loop line =
+           try
+             let subs = Pcre.exec ~rex:re_keyvalue line in
+             let key = Pcre.get_substring subs 1 in
+             let value = Pcre.get_substring subs 2 in
+             if key = "Name" then name := value
+             else if key = "Version" then version := value
+             else if key = "Release" then release := value
+             else if key = "Architecture" then arch := value;
+             let line = input_line chan in
+             loop line
+           with
+             Not_found | End_of_file ->
+               close_in chan
+         in
+         loop line;
+         let name, version, release, arch =
+           !name, !version, !release, !arch in
+         if name = "" || version = "" || release = "" || arch = "" then
+           failwith (sprintf "%s: missing Name, Version, Release or Architecture key" filename);
+         name, sprintf "%s-%s.%s" version release arch
+       ) in
+
+      printf "%s -> %s, %s\n" basename name version;
+
+      (basename, name, version)
+  ) infos in
+
+  ()