Experimental automated 'follower' code.
[virt-mem.git] / extract / codegen / code_generation.ml
1 (* Memory info command 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 open Camlp4.PreCast
21 open Syntax
22 (*open Ast*)
23
24 open ExtList
25 open ExtString
26 open Printf
27
28 module PP = Pahole_parser
29 module SC = Struct_classify
30
31 let rec uniq ?(cmp = Pervasives.compare) = function
32     [] -> []
33   | [x] -> [x]
34   | x :: y :: xs when cmp x y = 0 ->
35       uniq (x :: xs)
36   | x :: y :: xs ->
37       x :: uniq (y :: xs)
38
39 let sort_uniq ?cmp xs =
40   let xs = List.sort ?cmp xs in
41   let xs = uniq ?cmp xs in
42   xs
43
44 (* We don't care about locations when generating code, so it's
45  * useful to just have a single global _loc.
46  *)
47 let _loc = Loc.ghost
48
49 (* Some handy camlp4 construction functions which do some
50  * things that ought to be easy/obvious but aren't.
51  *
52  * 'concat_str_items' concatenates a list of str_item together into
53  * one big str_item.
54  *
55  * 'concat_record_fields' concatenates a list of records fields into
56  * a record.  The list must have at least one element.
57  *
58  * 'build_record' builds a record out of record fields.
59  * 
60  * 'build_tuple_from_exprs' builds an arbitrary length tuple from
61  * a list of expressions of length >= 2.
62  *
63  * Thanks to bluestorm on #ocaml for getting these working.
64  *)
65 let concat_str_items items =
66   match items with
67   | [] -> <:str_item< >>
68   | x :: xs ->
69       List.fold_left (fun xs x -> <:str_item< $xs$ $x$ >>) x xs
70
71 let concat_sig_items items =
72   match items with
73   | [] -> <:sig_item< >>
74   | x :: xs ->
75       List.fold_left (fun xs x -> <:sig_item< $xs$ $x$ >>) x xs
76
77 let concat_exprs exprs =
78   match exprs with
79   | [] -> assert false
80   | x :: xs ->
81       List.fold_left (fun xs x -> <:expr< $xs$ ; $x$ >>) x xs
82
83 let concat_record_fields fields =
84   match fields with
85     | [] -> assert false
86     | f :: fs ->
87         List.fold_left (fun fs f -> <:ctyp< $fs$ ; $f$ >>) f fs
88
89 let concat_record_bindings rbs =
90   match rbs with
91     | [] -> assert false
92     | rb :: rbs ->
93         List.fold_left (fun rbs rb -> <:rec_binding< $rbs$ ; $rb$ >>) rb rbs
94
95 let build_record rbs =
96   Ast.ExRec (_loc, rbs, Ast.ExNil _loc)
97
98 let build_tuple_from_exprs exprs =
99   match exprs with
100   | [] | [_] -> assert false
101   | x :: xs ->
102       Ast.ExTup (_loc,
103                  List.fold_left (fun xs x -> Ast.ExCom (_loc, x, xs)) x xs)
104
105 let build_tuple_from_patts patts =
106   match patts with
107   | [] | [_] -> assert false
108   | x :: xs ->
109       Ast.PaTup (_loc,
110                  List.fold_left (fun xs x -> Ast.PaCom (_loc, x, xs)) x xs)
111
112 type code = Ast.str_item * Ast.sig_item
113
114 let ocaml_type_of_field_type = function
115   | PP.FInteger -> <:ctyp< int64 >>
116   | PP.FString _ -> <:ctyp< string >>
117   | PP.FStructPointer _ | PP.FVoidPointer
118   | PP.FAnonListHeadPointer | PP.FListHeadPointer _ ->
119       <:ctyp< Virt_mem_mmap.addr >>
120
121 let generate_types xs =
122   let strs = List.map (
123     fun (struct_name, sflist, cflist) ->
124       let sflist = List.map (
125         fun { SC.sf_name = sf_name; sf_fields = fields } ->
126           if fields <> [] then (
127             let fields = List.map (
128               fun (name, t) ->
129                 let t = ocaml_type_of_field_type t in
130                 <:ctyp< $lid:sf_name^"_"^name$ : $t$ >>
131             ) fields in
132             let fields = concat_record_fields fields in
133
134             <:str_item<
135               type $lid:sf_name$ = { $fields$ }
136             >>
137           ) else
138             <:str_item< type $lid:sf_name$ = unit >>
139       ) sflist in
140       let sflist = concat_str_items sflist in
141
142       let cflist = List.map (
143         fun { SC.cf_name = cf_name; cf_fields = fields } ->
144           if fields <> [] then (
145             let fields = List.map (
146               fun (name, t) ->
147                 let t = ocaml_type_of_field_type t in
148                 <:ctyp< $lid:cf_name^"_"^name$ : $t$ >>
149             ) fields in
150             let fields = concat_record_fields fields in
151
152             <:str_item<
153               type $lid:cf_name$ = { $fields$ }
154             >>
155           ) else
156             <:str_item< type $lid:cf_name$ = unit >>
157       ) cflist in
158       let cflist = concat_str_items cflist in
159
160       <:str_item<
161         type ('a, 'b) $lid:struct_name$ = 'a * 'b ;;
162         $sflist$
163         $cflist$
164       >>
165   ) xs in
166
167   concat_str_items strs, <:sig_item< >>
168
169 let generate_offsets xs =
170   (* Only need to generate the offset_of_* functions for fields
171    * which are cross-referenced from another field.  Which
172    * ones are those?
173    *)
174   let fields =
175     List.concat (
176       List.map (
177         fun (_, (_, all_fields)) ->
178           List.filter_map (
179             function
180             | (_,
181                PP.FListHeadPointer ((Some (struct_name, field_name)) as f)) ->
182                 f
183             | _ ->
184                 None
185           ) all_fields
186       ) xs
187     ) in
188
189   let fields = sort_uniq fields in
190
191   let strs =
192     List.map (
193       fun (struct_name, field_name) ->
194         let kernels, _ =
195           try List.assoc struct_name xs
196           with Not_found ->
197             failwith (
198               sprintf "generate_offsets: structure %s not found. This is probably a list_head-related bug."
199                 struct_name
200             ) in
201         (* Find the offset of this field in each kernel version. *)
202         let offsets =
203           List.filter_map (
204             fun ({ PP.kernel_version = version },
205                  { PP.struct_fields = fields }) ->
206               try
207                 let field =
208                   List.find (fun { PP.field_name = name } -> field_name = name)
209                     fields in
210                 let offset = field.PP.field_offset in
211                 Some (version, offset)
212               with Not_found -> None
213           ) kernels in
214
215         if offsets = [] then
216           failwith (
217             sprintf "generate_offsets: field %s.%s not found in any kernel. This is probably a list_head-related bug."
218               struct_name field_name
219           );
220
221         (* Generate a map of kernel version to offset. *)
222         let map = List.fold_left (
223           fun map (version, offset) ->
224             <:expr< StringMap.add $str:version$ $`int:offset$ $map$ >>
225         ) <:expr< StringMap.empty >> offsets in
226
227         let code =
228           <:str_item<
229             let $lid:"offset_of_"^struct_name^"_"^field_name$ =
230               let map = $map$ in
231               fun kernel_version -> StringMap.find kernel_version map
232           >> in
233         code
234     ) fields in
235
236   let strs = concat_str_items strs in
237
238   strs, <:sig_item< >>
239
240 let generate_parsers xs =
241   let strs =
242     List.map (
243       fun (struct_name, palist) ->
244         let palist =
245           List.map (
246             fun { SC.pa_name = pa_name } ->
247               <:str_item<
248                 let $lid:pa_name$ kernel_version bits = $str:pa_name$
249               >>
250           ) palist in
251         concat_str_items palist
252     ) xs in
253
254   let strs = concat_str_items strs in
255
256   (* The shared parser functions.
257    * 
258    * We could include bitmatch statements directly in here, but
259    * what happens is that the macros get expanded here, resulting
260    * in (even more) unreadable generated code.  So instead just
261    * do a textual substitution later by post-processing the
262    * generated files.  Not type-safe, but we can't have
263    * everything.
264    *)
265   let subs = Hashtbl.create 13 in
266   List.iter (
267     fun (struct_name, palist) ->
268       List.iter (
269         fun ({ SC.pa_name = pa_name;
270                pa_endian = endian; pa_structure = structure;
271                pa_shape_field_struct = sf;
272                pa_content_field_struct = cf }) ->
273           (* Generate the code to match this structure. *)
274           let endian =
275             match endian with
276             | Bitstring.LittleEndian -> "littleendian"
277             | Bitstring.BigEndian -> "bigendian"
278             | _ -> assert false in
279           let patterns =
280             String.concat ";\n      " (
281               List.map (
282                 function
283                 | { PP.field_name = field_name;
284                     field_type = PP.FInteger;
285                     field_offset = offset;
286                     field_size = size } ->
287                     (* 'zero+' is a hack to force the type to int64. *)
288                     sprintf "%s : zero+%d : offset(%d), %s"
289                       field_name (size*8) (offset*8) endian
290
291                 | { PP.field_name = field_name;
292                     field_type = (PP.FStructPointer _
293                                   | PP.FVoidPointer
294                                   | PP.FAnonListHeadPointer
295                                   | PP.FListHeadPointer _);
296                     field_offset = offset;
297                     field_size = size } ->
298                     sprintf "%s : zero+%d : offset(%d), %s"
299                       field_name (size*8) (offset*8) endian
300
301                 | { PP.field_name = field_name;
302                     field_type = PP.FString width;
303                     field_offset = offset;
304                     field_size = size } ->
305                     sprintf "%s : %d : offset(%d), string"
306                       field_name (width*8) (offset*8)
307               ) structure.PP.struct_fields
308             ) in
309
310           let shape_assignments =
311             List.map (
312               fun (field_name, field_type) ->
313
314                 (* Go and look up the field offset in the correct kernel. *)
315                 let { PP.field_offset = offset } =
316                   List.find (fun { PP.field_name = name } -> field_name = name)
317                     structure.PP.struct_fields in
318
319                 (* Generate assignment code, if necessary we can adjust
320                  * the list_head.
321                  *)
322                 match field_type with
323                 | PP.FListHeadPointer None ->
324                     sprintf "%s_%s = (if %s <> 0L then Int64.sub %s %dL else %s)"
325                       sf.SC.sf_name field_name
326                       field_name
327                       field_name offset field_name
328
329                 | PP.FListHeadPointer (Some (other_struct_name,
330                                              other_field_name)) ->
331                     (* A reference to a field in another structure.  We don't
332                      * know the offset until runtime, so we have to call
333                      * offset_of_<struct>_<field> to find it.
334                      *)
335                     sprintf "%s_%s = (
336                       if %s <> 0L then (
337                         let offset = offset_of_%s_%s kernel_version in
338                         let offset = Int64.of_int offset in
339                         Int64.sub %s offset
340                       ) else %s
341                     )"
342                       sf.SC.sf_name field_name field_name
343                       other_struct_name other_field_name
344                       field_name field_name
345                 | _ ->
346                     sprintf "%s_%s = %s" sf.SC.sf_name field_name field_name
347             ) sf.SC.sf_fields in
348
349           let shape_assignments =
350             if shape_assignments = [] then "()"
351             else
352               "{ " ^ String.concat ";\n        " shape_assignments ^ " }" in
353
354           let content_assignments =
355             List.map (
356               fun (field_name, _) ->
357                 sprintf "%s_%s = %s" cf.SC.cf_name field_name field_name
358             ) cf.SC.cf_fields in
359
360           let content_assignments =
361             if content_assignments = [] then "()"
362             else
363               "{ " ^ String.concat ";\n        " content_assignments ^ " }" in
364
365           let code =
366             sprintf "
367   bitmatch bits with
368   | { %s } ->
369       let s =
370       %s in
371       let c =
372       %s in
373       (s, c)
374   | { _ } ->
375       raise (Virt_mem_types.ParseError (%S, %S, match_err))"
376               patterns shape_assignments content_assignments
377               struct_name pa_name in
378
379           Hashtbl.add subs pa_name code
380       ) palist;
381   ) xs;
382
383   (strs, <:sig_item< >>), subs
384
385 (* Helper functions to store things in a fixed-length tuple very efficiently.
386  * Note that the tuple length must be >= 2.
387  *)
388 type tuple = string list
389
390 let tuple_create fields : tuple = fields
391
392 (* Generates 'let _, _, resultpatt, _ = tupleexpr in body'. *)
393 let tuple_generate_extract fields field resultpatt tupleexpr body =
394   let patts = List.map (
395     fun name -> if name = field then resultpatt else <:patt< _ >>
396   ) fields in
397   let result = build_tuple_from_patts patts in
398   <:expr< let $result$ = $tupleexpr$ in $body$ >>
399
400 (* Generates '(fieldexpr1, fieldexpr2, ...)'. *)
401 let tuple_generate_construct fieldexprs =
402   build_tuple_from_exprs fieldexprs
403
404 type follower_t =
405   | Missing of string | Follower of string | KernelVersion of string
406
407 let generate_followers xs =
408   (* Tuple of follower functions, just a list of struct_names. *)
409   let follower_tuple = tuple_create (List.map fst xs) in
410
411   (* A shape-follow function for every structure/shape. *)
412   let strs = List.map (
413     fun (struct_name, (_, sflist, _, _)) ->
414       List.map (
415         fun { SC.sf_name = sf_name; sf_fields = fields } ->
416           let body = List.fold_right (
417             fun (name, typ) body ->
418               let follower_name =
419                 match typ with
420                 | PP.FListHeadPointer None -> struct_name
421                 | PP.FListHeadPointer (Some (struct_name, _)) -> struct_name
422                 | PP.FStructPointer struct_name -> struct_name
423                 | _ -> assert false in
424               tuple_generate_extract follower_tuple follower_name
425                 <:patt< f >> <:expr< followers >>
426                 <:expr<
427                   let map =
428                     f load followers map shape.$lid:sf_name^"_"^name$ in $body$
429                 >>
430           ) fields <:expr< map >> in
431
432           <:str_item<
433             let $lid:sf_name^"_follower"$ load followers map shape =
434               $body$
435           >>
436       ) sflist
437   ) xs in
438   let strs = List.concat strs in
439
440   (* A follower function for every kernel version / structure.  When this
441    * function is called starting at some known root, it will load every
442    * reachable kernel structure.
443    *)
444   let strs =
445     let common =
446       (* Share as much common code as possible to minimize generated
447        * code size and benefit i-cache.
448        *)
449       <:str_item<
450         let kv_follower kernel_version struct_name total_size
451             parserfn followerfn
452             load followers map addr =
453           if addr <> 0L && not (AddrMap.mem addr map) then (
454             let map = AddrMap.add addr (struct_name, total_size) map in
455             let bits = load struct_name addr total_size in
456             let shape, _ = parserfn kernel_version bits in
457             followerfn load followers map shape
458           )
459           else map
460       >> in
461
462     let fs =
463       List.map (
464         fun (struct_name, (kernels, _, sfhash, pahash)) ->
465           List.map (
466             fun ({ PP.kernel_version = version; kv_i = kv_i },
467                  { PP.struct_total_size = total_size }) ->
468               let { SC.pa_name = pa_name } = Hashtbl.find pahash version in
469               let { SC.sf_name = sf_name } = Hashtbl.find sfhash version in
470
471               let fname = sprintf "%s_kv%d_follower" struct_name kv_i in
472
473               <:str_item<
474                 let $lid:fname$ =
475                   kv_follower
476                     $str:version$ $str:struct_name$ $`int:total_size$
477                     $lid:pa_name$ $lid:sf_name^"_follower"$
478               >>
479           ) kernels
480       ) xs in
481
482     let strs = strs @ [ common ] @ List.concat fs in
483     strs in
484
485   (* A map from kernel versions to follower functions.
486    *
487    * For each struct, we have a list of kernel versions which contain
488    * that struct.  Some kernels are missing a particular struct, so
489    * that is turned into a ParseError exception.
490    *)
491   let strs =
492     let nr_kernels =
493       List.fold_left max 0
494         (List.map (fun (_, (kernels, _, _, _)) -> List.length kernels) xs) in
495     let nr_structs = List.length xs in
496     let array = Array.make_matrix nr_kernels (nr_structs+1) (Missing "") in
497     List.iteri (
498       fun si (struct_name, _) ->
499         for i = 0 to nr_kernels - 1 do
500           array.(i).(si+1) <- Missing struct_name
501         done
502     ) xs;
503     List.iteri (
504       fun si (struct_name, (kernels, _, _, _)) ->
505         List.iter (
506           fun ({ PP.kernel_version = version; kv_i = kv_i }, _) ->
507             array.(kv_i).(0) <- KernelVersion version;
508             array.(kv_i).(si+1) <-
509               Follower (sprintf "%s_kv%d_follower" struct_name kv_i)
510         ) kernels
511     ) xs;
512
513     let array = Array.map (
514       fun row ->
515         match Array.to_list row with
516         | [] | (Missing _|Follower _) :: _ -> assert false
517         | KernelVersion kernel_version :: followers -> kernel_version, followers
518     ) array in
519
520     let map = List.fold_left (
521       fun map (kernel_version, followers) ->
522         let followers = List.map (
523           function
524           | Follower fname ->
525               <:expr< $lid:fname$ >>
526
527           (* no follower for this kernel/struct combination *)
528           | Missing struct_name ->
529               <:expr<
530                 fun _ _ _ _ ->
531                   raise (
532                     Virt_mem_types.ParseError (
533                       $str:struct_name$, "follower_map", struct_missing_err
534                     )
535                   )
536               >>
537           | KernelVersion _ -> assert false
538         ) followers in
539         let followers = tuple_generate_construct followers in
540
541         <:expr< StringMap.add $str:kernel_version$ $followers$ $map$ >>
542     ) <:expr< StringMap.empty >> (Array.to_list array) in
543
544     let str =
545       <:str_item<
546         let follower_map = $map$
547       >> in
548     strs @ [ str ] in
549
550   (* Finally a publicly exposed follower function. *)
551   let strs =
552     let fs =
553       List.map (
554         fun (struct_name, (kernels, _, _, _)) ->
555           let fname = sprintf "%s_follower" struct_name in
556
557           let body =
558             tuple_generate_extract follower_tuple struct_name
559               <:patt< f >> <:expr< followers >>
560               <:expr<
561                 f load followers AddrMap.empty addr
562               >> in
563
564           <:str_item<
565             let $lid:fname$ kernel_version load addr =
566               let followers =
567                 try StringMap.find kernel_version follower_map
568                 with Not_found ->
569                   unknown_kernel_version kernel_version $str:struct_name$ in
570               $body$
571           >>
572       ) xs in
573
574     strs @ fs in
575
576   let sigs =
577     List.map (
578       fun (struct_name, _) ->
579         <:sig_item<
580           val $lid:struct_name^"_follower"$ :
581             kernel_version ->
582             (string -> Virt_mem_mmap.addr -> int -> Bitstring.bitstring) ->
583             Virt_mem_mmap.addr ->
584             (string * int) AddrMap.t
585           >>
586     ) xs in
587
588   concat_str_items strs, concat_sig_items sigs
589
590 let output_interf ~output_file types offsets parsers followers =
591   (* Some standard code that appears at the top of the interface file. *)
592   let prologue =
593     <:sig_item<
594       module AddrMap : sig
595         type key = Virt_mem_mmap.addr
596         type 'a t = 'a Map.Make(Int64).t
597         val empty : 'a t
598         val is_empty : 'a t -> bool
599         val add : key -> 'a -> 'a t -> 'a t
600         val find : key -> 'a t -> 'a
601         val remove : key -> 'a t -> 'a t
602         val mem : key -> 'a t -> bool
603         val iter : (key -> 'a -> unit) -> 'a t -> unit
604         val map : ('a -> 'b) -> 'a t -> 'b t
605         val mapi : (key -> 'a -> 'b) -> 'a t -> 'b t
606         val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
607         val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
608         val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
609       end ;;
610       type kernel_version = string ;;
611     >> in
612
613   let sigs =
614     concat_sig_items [ prologue; types; offsets; parsers; followers ] in
615   Printers.OCaml.print_interf ~output_file sigs
616
617 (* Finally generate the output files. *)
618 let re_subst = Pcre.regexp "^(.*)\"(\\w+_parser_\\d+)\"(.*)$"
619
620 let output_implem ~output_file types offsets parsers parser_subs followers =
621   (* Some standard code that appears at the top of the implementation file. *)
622   let prologue =
623     <:str_item<
624       module StringMap = Map.Make (String) ;;
625       module AddrMap = Map.Make (Int64) ;;
626       type kernel_version = string ;;
627
628       let match_err = "failed to match kernel structure" ;;
629       let struct_missing_err = "struct does not exist in this kernel version" ;;
630
631       let unknown_kernel_version version struct_name =
632         invalid_arg (Printf.sprintf "%s: unknown kernel version or
633 struct %s is not supported in this kernel.
634 Try a newer version of virt-mem, or if the guest is not from a
635 supported Linux distribution, see this page about adding support:
636   http://et.redhat.com/~rjones/virt-mem/faq.html\n"
637                        version struct_name) ;;
638
639       let zero = 0 ;;
640     >> in
641
642   let strs =
643     concat_str_items [ prologue; types; offsets; parsers; followers ] in
644
645   (* Write the new implementation to .ml.new file. *)
646   let new_output_file = output_file ^ ".new" in
647   Printers.OCaml.print_implem ~output_file:new_output_file strs;
648
649   (* Substitute the parser bodies in the output file. *)
650   let ichan = open_in new_output_file in
651   let ochan = open_out output_file in
652
653   output_string ochan "\
654 (* WARNING: This file and the corresponding mli (interface) are
655  * automatically generated by the extract/codegen/ program.
656  *
657  * Any edits you make to this file will be lost.
658  *
659  * To update this file from the latest kernel database, it is recommended
660  * that you do 'make update-kernel-structs'.
661  *)\n\n";
662
663   let rec loop () =
664     let line = input_line ichan in
665     let line =
666       if Pcre.pmatch ~rex:re_subst line then (
667         let subs = Pcre.exec ~rex:re_subst line in
668         let start = Pcre.get_substring subs 1 in
669         let template = Pcre.get_substring subs 2 in
670         let rest = Pcre.get_substring subs 3 in
671         let sub =
672           try Hashtbl.find parser_subs template
673           with Not_found -> assert false in
674         start ^ sub ^ rest
675       ) else line in
676     output_string ochan line; output_char ochan '\n';
677     loop ()
678   in
679   (try loop () with End_of_file -> ());
680
681   close_out ochan;
682   close_in ichan;
683
684   Unix.unlink new_output_file