module PP = Pahole_parser
module SC = Struct_classify
+let rec uniq ?(cmp = Pervasives.compare) = function
+ [] -> []
+ | [x] -> [x]
+ | x :: y :: xs when cmp x y = 0 ->
+ uniq (x :: xs)
+ | x :: y :: xs ->
+ x :: uniq (y :: xs)
+
+let sort_uniq ?cmp xs =
+ let xs = List.sort ?cmp xs in
+ let xs = uniq ?cmp xs in
+ xs
+
(* We don't care about locations when generating code, so it's
* useful to just have a single global _loc.
*)
Ast.ExTup (_loc,
List.fold_left (fun xs x -> Ast.ExCom (_loc, x, xs)) x xs)
+type code = Ast.str_item * Ast.sig_item
+
let ocaml_type_of_field_type = function
| PP.FInteger -> <:ctyp< int64 >>
| PP.FString _ -> <:ctyp< string >>
concat_str_items strs, concat_sig_items sigs
+let generate_offsets xs =
+ (* Only need to generate the offset_of_* functions for fields
+ * which are cross-referenced from another field. Which
+ * ones are those?
+ *)
+ let fields =
+ List.concat (
+ List.map (
+ fun (_, (_, all_fields)) ->
+ List.filter_map (
+ function
+ | (_,
+ PP.FListHeadPointer ((Some (struct_name, field_name)) as f)) ->
+ f
+ | _ ->
+ None
+ ) all_fields
+ ) xs
+ ) in
+
+ let fields = sort_uniq fields in
+
+ let strs =
+ List.map (
+ fun (struct_name, field_name) ->
+ let kernels, _ =
+ try List.assoc struct_name xs
+ with Not_found ->
+ failwith (
+ sprintf "generate_offsets: structure %s not found. This is probably a list_head-related bug."
+ struct_name
+ ) in
+ (* Find the offset of this field in each kernel version. *)
+ let offsets =
+ List.filter_map (
+ fun ({ PP.kernel_version = version },
+ { PP.struct_fields = fields }) ->
+ try
+ let field =
+ List.find (fun { PP.field_name = name } -> field_name = name)
+ fields in
+ let offset = field.PP.field_offset in
+ Some (version, offset)
+ with Not_found -> None
+ ) kernels in
+
+ if offsets = [] then
+ failwith (
+ sprintf "generate_offsets: field %s.%s not found in any kernel. This is probably a list_head-related bug."
+ struct_name field_name
+ );
+
+ (* Generate a map of kernel version to offset. *)
+ let map = List.fold_left (
+ fun map (version, offset) ->
+ <:expr< StringMap.add $str:version$ $`int:offset$ $map$ >>
+ ) <:expr< StringMap.empty >> offsets in
+
+ let code =
+ <:str_item<
+ let $lid:"offset_of_"^struct_name^"_"^field_name$ =
+ let map = $map$ in
+ fun kernel_version -> StringMap.find kernel_version map
+ >> in
+ code
+ ) fields in
+
+ let strs = concat_str_items strs in
+ let strs =
+ <:str_item<
+ module StringMap = Map.Make (String) ;;
+ $strs$
+ >> in
+
+ strs, <:sig_item< >>
+
let generate_parsers xs =
let strs =
List.map (
let palist =
List.map (
fun { SC.pa_name = pa_name } ->
- <:str_item< let $lid:pa_name$ bits = $str:pa_name$ >>
+ <:str_item<
+ let $lid:pa_name$ kernel_version bits = $str:pa_name$
+ >>
) palist in
concat_str_items palist
) xs in
sprintf "%s_%s = Int64.sub %s %dL"
sf.SC.sf_name field_name field_name offset
- | PP.FListHeadPointer (Some (other_struct_name, other_field_name)) ->
- let other_offset = 666 in
- sprintf "%s_%s = Int64.sub %s %dL"
- sf.SC.sf_name field_name field_name other_offset
-
+ | PP.FListHeadPointer (Some (other_struct_name,
+ other_field_name)) ->
+ (* A reference to a field in another structure. We don't
+ * know the offset until runtime, so we have to call
+ * offset_of_<struct>_<field> to find it.
+ *)
+ sprintf "%s_%s = (
+ let offset = offset_of_%s_%s kernel_version in
+ let offset = Int64.of_int offset in
+ Int64.sub %s offset
+ )"
+ sf.SC.sf_name field_name
+ other_struct_name other_field_name
+ field_name
| _ ->
sprintf "%s_%s = %s" sf.SC.sf_name field_name field_name
) sf.SC.sf_fields in
) palist;
) xs;
- strs, <:sig_item< >>, subs
+ (strs, <:sig_item< >>), subs
-let output_interf ~output_file types parsers =
- let sigs = concat_sig_items [ types; parsers ] in
+let output_interf ~output_file types offsets parsers =
+ let sigs = concat_sig_items [ types; offsets; parsers ] in
Printers.OCaml.print_interf ~output_file sigs
(* Finally generate the output files. *)
let re_subst = Pcre.regexp "^(.*)\"(\\w+_parser_\\d+)\"(.*)$"
-let output_implem ~output_file types parsers parser_subs =
+let output_implem ~output_file types offsets parsers parser_subs =
let new_output_file = output_file ^ ".new" in
- let strs = concat_str_items [ types; parsers ] in
+ let strs = concat_str_items [ types; offsets; parsers ] in
Printers.OCaml.print_implem ~output_file:new_output_file strs;
(* Substitute the parser bodies in the output file. *)