Script to generate the parsing code from kernel database.
[virt-mem.git] / extract / codegen / kerneldb_to_parser.ml
1 (* Memory info for virtual domains.
2    (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
3    http://libvirt.org/
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *)
19
20 (* This program takes the kernel database (in kernels/ in toplevel
21    directory) and generates parsing code for the various structures
22    in the kernel that we are interested in.
23
24    The output programs -- *.ml, *.mli files of generated code -- go
25    into lib/ at the toplevel, eg. lib/kernel_task_struct.ml
26
27    The stuff at the top of this file determine what structures
28    and fields we try to parse.
29 *)
30
31 let what = [
32   "task_struct", (
33     "struct task_struct",
34     [ "state"; "prio"; "normal_prio"; "static_prio"; "tasks"; "comm"]
35   );
36 ]
37
38 open ExtList
39 open ExtString
40 open Printf
41
42 let (//) = Filename.concat
43
44 let () =
45   let args = Array.to_list Sys.argv in
46
47   let kernelsdir, outputdir =
48     match args with
49     | [_;kd;od] -> kd,od
50     | _ ->
51         let arg0 = Filename.basename Sys.executable_name in
52         eprintf "%s - Turn kernels database into code modules.
53
54 Usage:
55   %s <kernelsdir> <outputdir>
56
57 Example (from toplevel of virt-mem source tree):
58   %s kernels/ lib/
59 " arg0 arg0 arg0;
60         exit 2 in
61
62   (* Get the *.info files from the kernels database. *)
63   let infos = Sys.readdir kernelsdir in
64   let infos = Array.to_list infos in
65   let infos = List.filter (fun name -> String.ends_with name ".info") infos in
66   let infos = List.map ((//) kernelsdir) infos in
67
68   (* Regular expressions.  We really really should use ocaml-mikmatch ... *)
69   let re_oldformat = Pcre.regexp "^RPM: \\d+: \\(build \\d+\\) ([-\\w]+) ([\\w.]+) ([\\w.]+) \\(.*?\\) (\\w+)" in
70   let re_keyvalue = Pcre.regexp "^(\\w+): (.*)" in
71
72   (* Parse in the *.info files.  These have historically had a few different
73    * formats that we need to support.
74    *)
75   let infos = List.map (
76     fun filename ->
77       (* Get the basename (for getting the .data file later on). *)
78       let basename = Filename.chop_suffix filename ".info" in
79
80       let chan = open_in filename in
81       let line = input_line chan in
82
83       let name, version =
84         if Pcre.pmatch ~rex:re_oldformat line then (
85           (* If the file starts with "RPM: \d+: ..." then it's the original
86            * format.  Everything in one line.
87            *)
88           let subs = Pcre.exec ~rex:re_oldformat line in
89           let name = Pcre.get_substring subs 1 in
90           let version = Pcre.get_substring subs 2 in
91           let release = Pcre.get_substring subs 3 in
92           let arch = Pcre.get_substring subs 4 in
93           close_in chan;
94           name, sprintf "%s-%s.%s" version release arch
95         ) else (
96           (* New-style "key: value" entries, up to end of file or the first
97            * blank line.
98            *)
99           let name, version, release, arch =
100             ref "", ref "", ref "", ref "" in
101           let rec loop line =
102             try
103               let subs = Pcre.exec ~rex:re_keyvalue line in
104               let key = Pcre.get_substring subs 1 in
105               let value = Pcre.get_substring subs 2 in
106               if key = "Name" then name := value
107               else if key = "Version" then version := value
108               else if key = "Release" then release := value
109               else if key = "Architecture" then arch := value;
110               let line = input_line chan in
111               loop line
112             with
113               Not_found | End_of_file ->
114                 close_in chan
115           in
116           loop line;
117           let name, version, release, arch =
118             !name, !version, !release, !arch in
119           if name = "" || version = "" || release = "" || arch = "" then
120             failwith (sprintf "%s: missing Name, Version, Release or Architecture key" filename);
121           name, sprintf "%s-%s.%s" version release arch
122         ) in
123
124       printf "%s -> %s, %s\n" basename name version;
125
126       (basename, name, version)
127   ) infos in
128
129   ()