1 (* Memory info for virtual domains.
2 (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
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.
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.
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.
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.
24 The output programs -- *.ml, *.mli files of generated code -- go
25 into lib/ at the toplevel, eg. lib/kernel_task_struct.ml
27 The stuff at the top of this file determine what structures
28 and fields we try to parse.
33 "struct task_struct {", "};", true,
34 [ "state"; "prio"; "normal_prio"; "static_prio";
35 "tasks.prev"; "tasks.next"; "comm"]
38 "struct mm_struct {", "};", true,
42 "struct net_device {", "};", true,
43 [ "name"; "dev_addr" ]
51 let (//) = Filename.concat
54 let args = Array.to_list Sys.argv in
56 let kernelsdir, outputdir =
60 let arg0 = Filename.basename Sys.executable_name in
61 eprintf "%s - Turn kernels database into code modules.
64 %s <kernelsdir> <outputdir>
66 Example (from toplevel of virt-mem source tree):
71 (* Get the *.info files from the kernels database. *)
72 let infos = Sys.readdir kernelsdir in
73 let infos = Array.to_list infos in
74 let infos = List.filter (fun name -> String.ends_with name ".info") infos in
75 let infos = List.map ((//) kernelsdir) infos in
77 (* Regular expressions. We really really should use ocaml-mikmatch ... *)
78 let re_oldformat = Pcre.regexp "^RPM: \\d+: \\(build \\d+\\) ([-\\w]+) ([\\w.]+) ([\\w.]+) \\(.*?\\) (\\w+)" in
79 let re_keyvalue = Pcre.regexp "^(\\w+): (.*)" in
81 (* Parse in the *.info files. These have historically had a few different
82 * formats that we need to support.
84 let infos = List.map (
86 (* Get the basename (for getting the .data file later on). *)
87 let basename = Filename.chop_suffix filename ".info" in
89 let chan = open_in filename in
90 let line = input_line chan in
92 (* Kernel version string. *)
94 if Pcre.pmatch ~rex:re_oldformat line then (
95 (* If the file starts with "RPM: \d+: ..." then it's the
96 * original Fedora format. Everything in one line.
98 let subs = Pcre.exec ~rex:re_oldformat line in
99 (* let name = Pcre.get_substring subs 1 in *)
100 let version = Pcre.get_substring subs 2 in
101 let release = Pcre.get_substring subs 3 in
102 let arch = Pcre.get_substring subs 4 in
104 (* XXX Map name -> PAE, hugemem etc. *)
105 (* name, *) sprintf "%s-%s.%s" version release arch
107 (* New-style "key: value" entries, up to end of file or the first
110 let (*name,*) version, release, arch =
111 (*ref "",*) ref "", ref "", ref "" in
114 let subs = Pcre.exec ~rex:re_keyvalue line in
115 let key = Pcre.get_substring subs 1 in
116 let value = Pcre.get_substring subs 2 in
117 (*if key = "Name" then name := value
118 else*) if key = "Version" then version := value
119 else if key = "Release" then release := value
120 else if key = "Architecture" then arch := value;
121 let line = input_line chan in
124 Not_found | End_of_file ->
128 let (*name,*) version, release, arch =
129 (*!name,*) !version, !release, !arch in
130 if (*name = "" ||*) version = "" || release = "" || arch = "" then
131 failwith (sprintf "%s: missing Name, Version, Release or Architecture key" filename);
132 (* XXX Map name -> PAE, hugemem etc. *)
133 (* name, *) sprintf "%s-%s.%s" version release arch
136 (*printf "%s -> %s\n%!" basename version;*)
141 (* For quick access to the opener strings, build a hash. *)
142 let openers = Hashtbl.create 13 in
144 fun (name, (opener, closer, _, _)) ->
145 Hashtbl.add openers opener (closer, name)
148 (* Now read the data files and parse out the structures of interest. *)
149 let datas = List.map (
150 fun (basename, version) ->
151 let file_exists name =
152 try Unix.access name [Unix.F_OK]; true
153 with Unix.Unix_error _ -> false
155 let close_process_in cmd chan =
156 match Unix.close_process_in chan with
157 | Unix.WEXITED 0 -> ()
159 eprintf "%s: command exited with code %d\n" cmd i; exit i
160 | Unix.WSIGNALED i ->
161 eprintf "%s: command exited with signal %d\n" cmd i; exit 1
163 eprintf "%s: command stopped by signal %d\n" cmd i; exit 1
166 (* Open the data file, uncompressing it on the fly if necessary. *)
168 if file_exists (basename ^ ".data") then
169 open_in (basename ^ ".data"), close_in
170 else if file_exists (basename ^ ".data.gz") then (
172 sprintf "gzip -cd %s" (Filename.quote (basename ^ ".data.gz")) in
173 Unix.open_process_in cmd, close_process_in cmd
175 else if file_exists (basename ^ ".data.bz2") then (
177 sprintf "bzip2 -cd %s" (Filename.quote (basename ^ ".data.bz2")) in
178 Unix.open_process_in cmd, close_process_in cmd
181 (sprintf "%s: cannot find corresponding data file" basename) in
183 (* Read the data file in, looking for structures of interest to us. *)
184 let bodies = Hashtbl.create 13 in
186 let line = input_line chan in
188 (* If the line is an opener for one of the structures we
189 * are looking for, then for now just save all the text until
190 * we get to the closer line.
193 let closer, name = Hashtbl.find openers line in
194 let rec loop2 lines =
195 let line = input_line chan in
196 let lines = line :: lines in
197 if String.starts_with line closer then List.rev lines
204 failwith (sprintf "%s: %s: %S not matched by closing %S" basename name line closer) in
206 Hashtbl.replace bodies name body
207 with Not_found -> ());
211 (try loop () with End_of_file -> ());
215 (* Make sure we got all the mandatory structures. *)
217 fun (name, (_, _, mandatory, _)) ->
218 if mandatory && not (Hashtbl.mem bodies name) then
219 failwith (sprintf "%s: structure %s not found in this kernel" basename name)
222 (basename, version, bodies)
225 (* Now parse each structure body.
226 * XXX This would be better as a proper lex/yacc parser.
227 * XXX Even better would be to have a proper interface to libdwarves.
229 let re_offsetsize = Pcre.regexp "/\\*\\s+(\\d+)\\s+(\\d+)\\s+\\*/" in
230 let re_intfield = Pcre.regexp "int\\s+(\\w+);" in
231 let re_ptrfield = Pcre.regexp "struct\\s+(\\w+)\\s*\\*\\s*(\\w+);" in
232 let re_strfield = Pcre.regexp "char\\s+(\\w+)\\[(\\d+)\\];" in
233 let re_structopener = Pcre.regexp "(struct|union)\\s+.*{$" in
234 let re_structcloser = Pcre.regexp "}\\s*(\\w+)?(\\[\\d+\\])?;" in
236 (* 'basename' is the source file, and second parameter ('body') is
237 * the list of text lines which covers this structure (minus the
238 * opener line). Result is the list of parsed fields from this
241 let rec parse basename = function
243 | [_] -> [] (* Just the closer line, finished. *)
244 | line :: lines when Pcre.pmatch ~rex:re_structopener line ->
245 (* Recursively parse a sub-structure. First search for the
246 * corresponding closer line.
248 let rec loop depth acc = function
250 eprintf "%s: %S has no matching close structure line\n%!"
253 | line :: lines when Pcre.pmatch ~rex:re_structopener line ->
254 loop (depth+1) (line :: acc) lines
256 when depth = 0 && Pcre.pmatch ~rex:re_structcloser line ->
259 when depth > 0 && Pcre.pmatch ~rex:re_structcloser line ->
260 loop (depth-1) (line :: acc) lines
261 | line :: lines -> loop depth (line :: acc) lines
263 let nested_body, rest = loop 0 [] lines in
265 (* Then parse the sub-structure. *)
266 let struct_name, nested_body =
267 match nested_body with
270 let subs = Pcre.exec ~rex:re_structcloser closer in
272 try Some (Pcre.get_substring subs 1) with Not_found -> None in
273 struct_name, List.rev nested_body in
274 let nested_fields = parse basename nested_body in
276 (* Prefix the sub-fields with the name of the structure. *)
278 match struct_name with
279 | None -> nested_fields
282 fun (name, details) -> (prefix ^ "." ^ name, details)
285 (* Parse the rest. *)
286 nested_fields @ parse basename rest
288 | line :: lines when Pcre.pmatch ~rex:re_intfield line ->
290 let subs = Pcre.exec ~rex:re_intfield line in
291 let name = Pcre.get_substring subs 1 in
293 let subs = Pcre.exec ~rex:re_offsetsize line in
294 let offset = int_of_string (Pcre.get_substring subs 1) in
295 let size = int_of_string (Pcre.get_substring subs 2) in
296 (name, (`Int, offset, size)) :: parse basename lines
298 Not_found -> parse basename lines
301 | line :: lines when Pcre.pmatch ~rex:re_ptrfield line ->
302 (* A pointer-to-struct field. *)
303 let subs = Pcre.exec ~rex:re_ptrfield line in
304 let struct_name = Pcre.get_substring subs 1 in
305 let name = Pcre.get_substring subs 2 in
307 let subs = Pcre.exec ~rex:re_offsetsize line in
308 let offset = int_of_string (Pcre.get_substring subs 1) in
309 let size = int_of_string (Pcre.get_substring subs 2) in
310 (name, (`Ptr struct_name, offset, size)) :: parse basename lines
312 Not_found -> parse basename lines
315 | line :: lines when Pcre.pmatch ~rex:re_strfield line ->
316 (* A string (char array) field. *)
317 let subs = Pcre.exec ~rex:re_strfield line in
318 let name = Pcre.get_substring subs 1 in
319 let width = int_of_string (Pcre.get_substring subs 2) in
321 let subs = Pcre.exec ~rex:re_offsetsize line in
322 let offset = int_of_string (Pcre.get_substring subs 1) in
323 let size = int_of_string (Pcre.get_substring subs 2) in
324 (name, (`Str width, offset, size)) :: parse basename lines
326 Not_found -> parse basename lines
330 (* Just ignore any other field we can't parse. *)
335 let datas = List.map (
336 fun (basename, version, bodies) ->
337 let structures = List.filter_map (
338 fun (name, (_, _, _, wanted_fields)) ->
340 try Some (Hashtbl.find bodies name) with Not_found -> None in
344 let body = List.tl body in (* Don't care about opener line. *)
345 let fields = parse basename body in
347 (* That got us all the fields, but we only care about
350 let fields = List.filter (
351 fun (name, _) -> List.mem name wanted_fields
354 (* Also check we have all the wanted fields. *)
357 if not (List.mem_assoc wanted_field fields) then
358 failwith (sprintf "%s: structure %s is missing required field %s" basename name wanted_field)
364 (basename, version, structures)
367 (* If you're debugging, uncomment this to print out the parsed
372 fun (basename, version, structures) ->
373 printf "%s (version: %s):\n" basename version;
375 fun (struct_name, fields) ->
376 printf " struct %s {\n" struct_name;
378 fun (field_name, (typ, offset, size)) ->
380 | `Int -> printf " int %s; " field_name
381 | `Ptr struct_name ->
382 printf " struct %s *%s; " struct_name field_name
384 printf " char %s[%d]; " field_name width
386 printf " /* offset = %d, size = %d */\n" offset size
393 (* Let's generate some code! *)