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.
22 open Virt_mem_gettext.Gettext
27 (* The implementation of 'ps' has gone through a number of complete
28 * rewrites. This explains the "leading theories" of how to implement
29 * this. Probably some sort of hybrid method is the way to go.
31 * General comments: The init_task ksym points to the initial
32 * task_struct (PID 0, swapper). Out from this init task_struct
33 * we find linked lists (struct list_head) which cover all
34 * tasks and subsets of the tasks (eg. parent/children).
35 * So if we have init_task and we know what the task_struct
36 * looks like, then we can follow these chains of pointers
37 * to find all processes in the kernel.
39 * task_struct varies greatly depending on: word size, kernel
40 * version, CONFIG_* settings, and vendor/additional patches.
42 * (Theory 1) Precompiled task_struct. We can easily and reliably
43 * determine the Linux kernel version (see virt-uname). In
44 * theory we could compile a list of known kernel versions,
45 * check out their sources beforehand, and find the absolute
46 * layout of the task_struct (eg. using CIL). This method would
47 * only work for known kernel versions, but has the advantage
48 * that all fields in the task_struct would be known.
50 * (Theory 2) Fuzzy matched task_struct. The task_struct has
51 * a certain internal structure which is stable even over many
52 * kernel revisions. For example, groups of pointers always
53 * occur together. We search through init_task looking for
54 * these characteristic features and where a pointer is found
55 * to another task_struct we search that (recursively) on the
56 * assumption that those contain the same features at the same
57 * location. This works well for pointers, but not so well for
58 * finding other fields (eg. uids, process name, etc). We can
59 * defray the cost of searches by caching the results between
63 (* This accumulator stores a map of task_struct address to
64 * task_struct content. It can also store the address of a
65 * task_struct which isn't fully parsed yet.
66 * key: address (int64)
67 * value: task_struct option (None if not parsed yet)
74 include Map.Make (Int64)
76 let contains_addr accum addr = mem addr accum
77 let add_address accum addr = add addr None accum
78 let add_task_struct accum addr ts = add addr (Some ts) accum
81 exception ShapeError of int
83 (* Parse (recursively) all task_structs, starting at init_task.
85 * Either returns an accumulator of task_structs, or raises an
88 * On failure, the caller can try again with a different shape.
89 * The exception gives some information back to the caller about
90 * where the match failed, allowing faster, directed searches.
92 let get_task_struct debug mem ((ws,e) as wse) ((n1,n2) as shape)
95 (* The 'struct list_head', the standard double-linked list used
96 * by Linux, contains next and prev pointers. However these point
97 * not to the beginning of the struct, but to the offset of the
98 * list_head within the struct. This function adjusts the pointer
99 * to get back to the beginning of the struct.
101 let container_of addr bitoffset =
103 let offset = Int64.of_int (bitoffset lsr 3) in
108 (* Do get_task_struct as a recursive subfunction so we don't have
109 * to pass around all the fixed arguments on the stack.
111 let rec get_task_struct ~i addr accum =
112 if Accum.contains_addr accum addr then accum
114 (* NOTE: The order of the following three statements is crucial
115 * to avoid cycles and malicious guests. Do not change it!
118 try Bitstring.bitstring_of_string (get_bytes mem addr (4096*8))
119 with Invalid_argument "get_bytes" -> raise (ShapeError i) in
120 let accum = Accum.add_address accum addr in
121 let accum = ref accum in
124 eprintf "trying to match task_struct, i=%d %Lx\n%!" i addr;
127 | { _ : n1*ws : bitstring; (* Ignore start of task_struct. *)
129 (* struct list_head tasks *)
130 tasks_next : ws : endian(e),
131 save_offset_to (offset),
132 bind (let addr = container_of tasks_next offset in
133 eprintf "offset = %d, tasks_next = %Lx, addr = %Lx\n"
134 offset tasks_next addr;
135 accum := get_task_struct ~i:1 addr !accum;
137 tasks_prev : ws : endian(e),
138 bind (let addr = container_of tasks_prev offset in
139 eprintf "offset = %d, tasks_prev = %Lx, addr = %Lx\n"
140 offset tasks_prev addr;
141 accum := get_task_struct ~i:1 addr !accum;
144 _ : n2*ws : bitstring;
146 (* struct task_struct *parent *)
147 parent : ws : endian(e),
148 bind (let addr = parent in
149 accum := get_task_struct ~i:2 addr !accum;
152 (* struct list_head children *)
153 children_next : ws : endian(e),
154 save_offset_to (offset),
155 bind (let addr = container_of children_next offset in
156 accum := get_task_struct ~i:2 addr !accum;
159 children_prev : ws : endian(e),
160 bind (let addr = container_of children_prev offset in
161 accum := get_task_struct ~i:2 addr !accum;
164 (* struct list_head sibling *)
165 sibling_next : ws : endian(e),
166 save_offset_to (offset),
167 bind (let addr = container_of sibling_next offset in
168 accum := get_task_struct ~i:2 addr !accum;
171 sibling_prev : ws : endian(e),
172 bind (let addr = container_of sibling_prev offset in
173 accum := get_task_struct ~i:2 addr !accum;
176 (* struct task_struct *group_leader *)
177 group_leader : ws : endian(e),
178 bind (let addr = group_leader in
179 accum := get_task_struct ~i:2 addr !accum;
182 (* Successful match, so return the updated accumulator. *)
183 let accum = !accum in
184 let accum = Accum.add_task_struct accum addr { ts_addr = addr } in
188 (* Unsuccessful match, throw an exception. *)
189 raise (ShapeError (-1))
192 get_task_struct ~i:0 addr accum
194 (* This is the directed search function. *)
195 let search debug mem ksymmap =
196 let ws = get_wordsize mem in
197 let ws = match ws with W32 -> 32 | W64 -> 64 in
198 let e = get_endian mem in
202 try Ksymmap.find "init_task" ksymmap
204 eprintf "virt-ps: cannot find kernel symbol 'init_task'\n";
207 let accum = Accum.empty in
211 if debug then eprintf "search: trying (%d, %d)\n" n1 n2;
212 let ts = get_task_struct debug mem wse (n1, n2) init_task accum in
213 if debug then eprintf "search: success (%d, %d)\n" n1 n2;
216 | ShapeError ((-1|0|1) as i) ->
217 if debug then eprintf "search: ShapeError %d\n" i;
220 if debug then eprintf "search: ShapeError 2\n";
226 let run debug ({ mem = mem }, ksymmap, _) =
227 search debug mem ksymmap
229 let summary = s_"list processes in virtual machine"
230 let description = s_"\
231 virt-ps prints a process listing for virtual machines running under
234 let () = Virt_mem.register "ps" summary description ~run