1 (* Memory info command 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.
28 module PP = Pahole_parser
29 module SC = Struct_classify
31 let rec uniq ?(cmp = Pervasives.compare) = function
34 | x :: y :: xs when cmp x y = 0 ->
39 let sort_uniq ?cmp xs =
40 let xs = List.sort ?cmp xs in
41 let xs = uniq ?cmp xs in
44 (* We don't care about locations when generating code, so it's
45 * useful to just have a single global _loc.
49 (* Some handy camlp4 construction functions which do some
50 * things that ought to be easy/obvious but aren't.
52 * 'concat_str_items' concatenates a list of str_item together into
55 * 'concat_record_fields' concatenates a list of records fields into
56 * a record. The list must have at least one element.
58 * 'build_record' builds a record out of record fields.
60 * 'build_tuple_from_exprs' builds an arbitrary length tuple from
61 * a list of expressions of length >= 2.
63 * Thanks to bluestorm on #ocaml for getting these working.
65 let concat_str_items items =
67 | [] -> <:str_item< >>
69 List.fold_left (fun xs x -> <:str_item< $xs$ $x$ >>) x xs
71 let concat_sig_items items =
73 | [] -> <:sig_item< >>
75 List.fold_left (fun xs x -> <:sig_item< $xs$ $x$ >>) x xs
77 let concat_record_fields fields =
81 List.fold_left (fun fs f -> <:ctyp< $fs$ ; $f$ >>) f fs
83 let concat_record_bindings rbs =
87 List.fold_left (fun rbs rb -> <:rec_binding< $rbs$ ; $rb$ >>) rb rbs
89 let build_record rbs =
90 Ast.ExRec (_loc, rbs, Ast.ExNil _loc)
92 let build_tuple_from_exprs exprs =
94 | [] | [_] -> assert false
97 List.fold_left (fun xs x -> Ast.ExCom (_loc, x, xs)) x xs)
99 type code = Ast.str_item * Ast.sig_item
101 let ocaml_type_of_field_type = function
102 | PP.FInteger -> <:ctyp< int64 >>
103 | PP.FString _ -> <:ctyp< string >>
104 | PP.FStructPointer _ | PP.FVoidPointer
105 | PP.FAnonListHeadPointer | PP.FListHeadPointer _ ->
106 <:ctyp< Virt_mem_mmap.addr >>
108 let generate_types xs =
109 let strs = List.map (
110 fun (struct_name, sflist, cflist) ->
111 let sflist = List.map (
112 fun { SC.sf_name = sf_name; sf_fields = fields } ->
113 if fields <> [] then (
114 let fields = List.map (
115 fun { PP.field_name = name; PP.field_type = t } ->
116 let t = ocaml_type_of_field_type t in
117 <:ctyp< $lid:sf_name^"_"^name$ : $t$ >>
119 let fields = concat_record_fields fields in
122 type $lid:sf_name$ = { $fields$ }
125 <:str_item< type $lid:sf_name$ = unit >>
127 let sflist = concat_str_items sflist in
129 let cflist = List.map (
130 fun { SC.cf_name = cf_name; cf_fields = fields } ->
131 if fields <> [] then (
132 let fields = List.map (
133 fun { PP.field_name = name; PP.field_type = t } ->
134 let t = ocaml_type_of_field_type t in
135 <:ctyp< $lid:cf_name^"_"^name$ : $t$ >>
137 let fields = concat_record_fields fields in
140 type $lid:cf_name$ = { $fields$ }
143 <:str_item< type $lid:cf_name$ = unit >>
145 let cflist = concat_str_items cflist in
148 type ('a, 'b) $lid:struct_name$ = {
149 $lid:struct_name^"_shape"$ : 'a;
150 $lid:struct_name^"_content"$ : 'b;
159 fun (struct_name, _, _) ->
161 type ('a, 'b) $lid:struct_name$
165 concat_str_items strs, concat_sig_items sigs
167 let generate_offsets xs =
168 (* Only need to generate the offset_of_* functions for fields
169 * which are cross-referenced from another field. Which
175 fun (_, (_, all_fields)) ->
179 PP.FListHeadPointer ((Some (struct_name, field_name)) as f)) ->
187 let fields = sort_uniq fields in
191 fun (struct_name, field_name) ->
193 try List.assoc struct_name xs
196 sprintf "generate_offsets: structure %s not found. This is probably a list_head-related bug."
199 (* Find the offset of this field in each kernel version. *)
202 fun ({ PP.kernel_version = version },
203 { PP.struct_fields = fields }) ->
206 List.find (fun { PP.field_name = name } -> field_name = name)
208 let offset = field.PP.field_offset in
209 Some (version, offset)
210 with Not_found -> None
215 sprintf "generate_offsets: field %s.%s not found in any kernel. This is probably a list_head-related bug."
216 struct_name field_name
219 (* Generate a map of kernel version to offset. *)
220 let map = List.fold_left (
221 fun map (version, offset) ->
222 <:expr< StringMap.add $str:version$ $`int:offset$ $map$ >>
223 ) <:expr< StringMap.empty >> offsets in
227 let $lid:"offset_of_"^struct_name^"_"^field_name$ =
229 fun kernel_version -> StringMap.find kernel_version map
234 let strs = concat_str_items strs in
237 module StringMap = Map.Make (String) ;;
243 let generate_parsers xs =
246 fun (struct_name, palist) ->
249 fun { SC.pa_name = pa_name } ->
251 let $lid:pa_name$ kernel_version bits = $str:pa_name$
254 concat_str_items palist
257 let strs = concat_str_items strs in
260 let match_err = "failed to match kernel structure" ;;
265 (* The shared parser functions.
267 * We could include bitmatch statements directly in here, but
268 * what happens is that the macros get expanded here, resulting
269 * in (even more) unreadable generated code. So instead just
270 * do a textual substitution later by post-processing the
271 * generated files. Not type-safe, but we can't have
274 let subs = Hashtbl.create 13 in
276 fun (struct_name, palist) ->
278 fun ({ SC.pa_name = pa_name;
279 pa_endian = endian; pa_structure = structure;
280 pa_shape_field_struct = sf;
281 pa_content_field_struct = cf }) ->
282 (* Generate the code to match this structure. *)
285 | Bitstring.LittleEndian -> "littleendian"
286 | Bitstring.BigEndian -> "bigendian"
287 | _ -> assert false in
289 String.concat ";\n " (
292 | { PP.field_name = field_name;
293 field_type = PP.FInteger;
294 field_offset = offset;
295 field_size = size } ->
296 (* 'zero+' is a hack to force the type to int64. *)
297 sprintf "%s : zero+%d : offset(%d), %s"
298 field_name (size*8) (offset*8) endian
300 | { PP.field_name = field_name;
301 field_type = (PP.FStructPointer _
303 | PP.FAnonListHeadPointer
304 | PP.FListHeadPointer _);
305 field_offset = offset;
306 field_size = size } ->
307 sprintf "%s : zero+%d : offset(%d), %s"
308 field_name (size*8) (offset*8) endian
310 | { PP.field_name = field_name;
311 field_type = PP.FString width;
312 field_offset = offset;
313 field_size = size } ->
314 sprintf "%s : %d : offset(%d), string"
315 field_name (width*8) (offset*8)
316 ) structure.PP.struct_fields
319 let shape_assignments =
321 fun { PP.field_name = field_name;
322 field_type = field_type;
323 field_offset = offset } ->
325 match field_type with
326 | PP.FListHeadPointer None ->
327 sprintf "%s_%s = Int64.sub %s %dL"
328 sf.SC.sf_name field_name field_name offset
330 | PP.FListHeadPointer (Some (other_struct_name,
331 other_field_name)) ->
332 (* A reference to a field in another structure. We don't
333 * know the offset until runtime, so we have to call
334 * offset_of_<struct>_<field> to find it.
337 let offset = offset_of_%s_%s kernel_version in
338 let offset = Int64.of_int offset in
341 sf.SC.sf_name field_name
342 other_struct_name other_field_name
345 sprintf "%s_%s = %s" sf.SC.sf_name field_name field_name
348 let shape_assignments =
349 if shape_assignments = [] then "()"
351 "{ " ^ String.concat ";\n " shape_assignments ^ " }" in
353 let content_assignments =
355 fun { PP.field_name = field_name } ->
356 sprintf "%s_%s = %s" cf.SC.cf_name field_name field_name
359 let content_assignments =
360 if content_assignments = [] then "()"
362 "{ " ^ String.concat ";\n " content_assignments ^ " }" in
372 { %s_shape = shape; %s_content = content }
374 raise (Virt_mem_types.ParseError (%S, %S, match_err))"
375 patterns shape_assignments content_assignments
376 struct_name struct_name
377 struct_name pa_name in
379 Hashtbl.add subs pa_name code
383 (strs, <:sig_item< >>), subs
385 let output_interf ~output_file types offsets parsers =
386 let sigs = concat_sig_items [ types; offsets; parsers ] in
387 Printers.OCaml.print_interf ~output_file sigs
389 (* Finally generate the output files. *)
390 let re_subst = Pcre.regexp "^(.*)\"(\\w+_parser_\\d+)\"(.*)$"
392 let output_implem ~output_file types offsets parsers parser_subs =
393 let new_output_file = output_file ^ ".new" in
395 let strs = concat_str_items [ types; offsets; parsers ] in
396 Printers.OCaml.print_implem ~output_file:new_output_file strs;
398 (* Substitute the parser bodies in the output file. *)
399 let ichan = open_in new_output_file in
400 let ochan = open_out output_file in
402 output_string ochan "\
403 (* WARNING: This file and the corresponding mli (interface) are
404 * automatically generated by the extract/codegen/ program.
406 * Any edits you make to this file will be lost.
408 * To update this file from the latest kernel database, it is recommended
409 * that you do 'make update-kernel-structs'.
413 let line = input_line ichan in
415 if Pcre.pmatch ~rex:re_subst line then (
416 let subs = Pcre.exec ~rex:re_subst line in
417 let start = Pcre.get_substring subs 1 in
418 let template = Pcre.get_substring subs 2 in
419 let rest = Pcre.get_substring subs 3 in
421 try Hashtbl.find parser_subs template
422 with Not_found -> assert false in
425 output_string ochan line; output_char ochan '\n';
428 (try loop () with End_of_file -> ());
433 Unix.unlink new_output_file