virt-ps working again.
[virt-mem.git] / extract / codegen / pahole_parser.ml
index 1798b44..029ecde 100644 (file)
@@ -27,6 +27,7 @@ let (//) = Filename.concat
 type pathname = string
 
 type info = {
+  kv_i : int;
   kernel_version : string;
   arch : string;
   basename : string;
@@ -48,12 +49,13 @@ and field = {
 and f_type =
   | FStructPointer of string
   | FVoidPointer
-  | FListHeadPointer
+  | FAnonListHeadPointer
+  | FListHeadPointer of (string * string) option
   | FInteger
   | FString of int
 
 let string_of_info i =
-  sprintf "%s: %s %s" i.basename i.kernel_version i.arch
+  sprintf "%s: %s (%d) %s" i.basename i.kernel_version i.kv_i i.arch
 
 let rec string_of_structure s =
   let fields = List.map string_of_field s.struct_fields in
@@ -63,13 +65,17 @@ let rec string_of_structure s =
 
 and string_of_field f =
   sprintf "%s %s; /* offset = %d, size = %d */"
-    f.field_name (string_of_f_type f.field_type)
+    (string_of_f_type f.field_type) f.field_name
     f.field_offset f.field_size
 
 and string_of_f_type = function
-  | FStructPointer struct_name -> sprintf "struct %s*" struct_name
+  | FStructPointer struct_name -> sprintf "struct %s *" struct_name
   | FVoidPointer -> "void *"
-  | FListHeadPointer -> "struct list_head *"
+  | FAnonListHeadPointer -> "struct list_head *"
+  | FListHeadPointer None ->
+      sprintf "struct /* self */ list_head *"
+  | FListHeadPointer (Some (struct_name, field_name)) ->
+      sprintf "struct /* to %s.%s */ list_head *" struct_name field_name
   | FInteger -> "int"
   | FString width -> sprintf "char[%d]" width
 
@@ -87,8 +93,8 @@ let list_kernels path =
   (* Parse in the *.info files.  These have historically had a few different
    * formats that we need to support.
    *)
-  let infos = List.map (
-    fun filename ->
+  let infos = List.mapi (
+    fun filename ->
       (* Get the basename (for getting the .data file later on). *)
       let basename = Filename.chop_suffix filename ".info" in
 
@@ -141,7 +147,8 @@ let list_kernels path =
 
       (*printf "%s -> %s %s\n%!" basename version arch;*)
 
-      { basename = basename; arch = arch;
+      { kv_i = i;
+       basename = basename; arch = arch;
        kernel_version = version }
   ) infos in
   infos
@@ -316,7 +323,7 @@ let load_structures { basename = basename } struct_names =
           if struct_name <> "list_head" then
             FStructPointer struct_name
           else
-            FListHeadPointer in
+            FAnonListHeadPointer in
         let field =
           { field_name = name; field_type = field_type;
             field_offset = offset; field_size = size } in
@@ -382,9 +389,100 @@ let load_structures { basename = basename } struct_names =
            ) fields in
            List.fold_left max 0 fields in
 
-         Some { struct_name = struct_name;
-                struct_fields = fields;
-                struct_total_size = total_size }
+         (* Sort the structure fields by field offset.  They are
+          * probably already in this order, but just make sure.
+          *)
+         let cmp { field_offset = o1 } { field_offset = o2 } = compare o1 o2 in
+         let fields = List.sort ~cmp fields in
+
+         Some (
+           struct_name,
+           { struct_name = struct_name;
+             struct_fields = fields;
+             struct_total_size = total_size }
+         )
   ) struct_names in
 
   structures
+
+(* XXX This loop is O(n^3), luckily n is small! *)
+let transpose good_struct_names kernels =
+  List.map (
+    fun struct_name ->
+      let kernels =
+       List.filter_map (
+         fun (info, structures) ->
+           try
+             let s = List.assoc struct_name structures in
+             Some (info, s)
+           with
+             Not_found -> None
+       ) kernels in
+
+      (* Sort the kernels, which makes the generated output more stable
+       * and makes patches more useful.
+       *)
+      let kernels = List.sort kernels in
+
+      struct_name, kernels
+  ) good_struct_names
+
+let get_fields structures =
+  (* Use a hash table to accumulate the fields as we find them.
+   * The key is the field name.  The value is the field type and the
+   * kernel version where first seen (for error reporting).  If
+   * we meet the field again, we check its type hasn't changed.
+   * Finally, we can use the hash to pull out all field names and
+   * types.
+   *)
+  let h = Hashtbl.create 13 in
+
+  (* A hash to check for fields which aren't always available by
+   * counting the number of times we see each field.
+   *)
+  let count, get =
+    let h = Hashtbl.create 13 in
+    let count field_name =
+      let r =
+       try Hashtbl.find h field_name
+       with Not_found -> let r = ref 0 in Hashtbl.add h field_name r; r in
+      incr r
+    in
+    let get field_name = try !(Hashtbl.find h field_name) with Not_found -> 0 in
+    count, get
+  in
+
+  List.iter (
+    fun ({kernel_version = version},
+        {struct_name = struct_name; struct_fields = fields}) ->
+      List.iter (
+       fun {field_name = name; field_type = typ} ->
+         count name;
+         try
+           let (field_type, version_first_seen) = Hashtbl.find h name in
+           if typ <> field_type then (
+             eprintf "Error: %s.%s: field changed type between kernel versions.\n"
+               struct_name name;
+             eprintf "In version %s it had type %s.\n"
+               version_first_seen (string_of_f_type field_type);
+             eprintf "In version %s it had type %s.\n"
+               version (string_of_f_type typ);
+             eprintf "The code cannot handle fields which change type like this.\n";
+             eprintf "See extract/codegen/pahole_parser.mli for more details.\n";
+             exit 1
+           )
+         with Not_found ->
+           Hashtbl.add h name (typ, version)
+      ) fields
+  ) structures;
+
+  let nr_kernels = List.length structures in
+
+  let fields =
+    Hashtbl.fold (
+      fun name (typ, _) fields ->
+       let always_available = get name = nr_kernels in
+       (name, (typ, always_available)) :: fields
+    ) h [] in
+
+  List.sort fields