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.
25 module C = Libvirt.Connect
26 module D = Libvirt.Domain
28 open Virt_mem_gettext.Gettext
30 module MMap = Virt_mem_mmap
32 let min_kallsyms_tabsize = 1_000L
33 let max_kallsyms_tabsize = 250_000L
35 (* Make the kernel size around 16 MB, but just a bit smaller than
36 * maximum string length so we can still run this on a 32 bit platform.
39 if Sys.word_size = 32 then Sys.max_string_length
41 let max_memory_peek = 65536 (* XXX Use D.max_peek function *)
48 * Virt_mem_utils.architecture
49 * ([`Wordsize], [`Endian]) Virt_mem_mmap.t
51 type image_with_ksyms =
54 * Virt_mem_utils.architecture
55 * ([`Wordsize], [`Endian]) Virt_mem_mmap.t
59 | Compressed of (string * MMap.addr) list * MMap.addr
60 | Uncompressed of (string * MMap.addr) list
62 (* When tools register themselves, they are added to this list.
63 * Later, we will alphabetize the list.
67 (* Registration function used by the tools. *)
68 let register ?(external_cmd = true) ?(extra_args = [])
69 ?argcheck ?beforeksyms ?run
70 name summary description =
72 (name, (name, summary, description, external_cmd, extra_args,
73 argcheck, beforeksyms, run))
76 (* Main program, called from mem/virt_mem_main.ml when all the
77 * tools have had a chance to register themselves.
80 (* Get the registered tools, alphabetically. *)
82 let tools = List.sort ~cmp:(fun (a,_) (b,_) -> compare a b) tools in
84 (* Which tool did the user want to run? Look at the executable
85 * name (eg. 'virt-dmesg' => tool == dmesg). If we don't recognise
86 * the executable name then we must look for the first parameter
87 * which doesn't begin with a '-' character.
89 * Note that we must do all of this before using the OCaml Arg
90 * module to properly parse the command line (below), so that
91 * we can have a usage message ready.
93 let tool, ignore_first_anon_arg =
94 let prog = Sys.executable_name in (* eg. "/usr/bin/virt-dmesg.opt" *)
95 let prog = Filename.basename prog in(* eg. "virt-dmesg.opt" *)
96 let prog = (* eg. "virt-dmesg" *)
97 try Filename.chop_extension prog with Invalid_argument _ -> prog in
98 let prog = (* eg. "dmesg" *)
99 if String.starts_with prog "virt-" then
100 String.sub prog 5 (String.length prog - 5)
102 try Some (List.assoc prog tools), false
104 let arg1 = (* First non-option argument. *)
105 match Array.to_list Sys.argv with
108 let rec loop = function
110 | a::args when String.length a > 0 && a.[0] = '-' -> loop args
115 | None -> None, false
116 | Some prog -> (* Recognisable first argument? *)
118 try Filename.chop_extension prog with Invalid_argument _ -> prog in
120 if String.starts_with prog "virt-" then
121 String.sub prog 5 (String.length prog - 5)
123 (try Some (List.assoc prog tools), true
124 with Not_found -> None, false) in
126 (* Make a usage message. *)
129 | None -> (* Generic usage message. *)
130 let tools = List.map (
131 fun (name, (_, summary, _, external_cmd, _, _, _, _)) ->
132 if external_cmd then "virt-"^name, summary
133 else "virt-mem "^name, summary
135 (* Maximum width of field in the left hand column. *)
137 List.fold_left max 0 (List.map String.length (List.map fst tools)) in
138 let tools = List.map (fun (l,r) -> pad max_width l, r) tools in
139 let tools = List.map (fun (l,r) -> " " ^ l ^ " - " ^ r) tools in
140 let tools = String.concat "\n" tools in
144 virt-mem: Tools for providing information about virtual machines
146 Currently available tools include:
150 <tool> [-options] [domains...]
152 To display extra help for a single tool, do:
157 (* Tool-specific usage message. *)
158 | Some (name, summary, description, external_cmd, _, _, _, _) ->
160 if external_cmd then "virt-" ^ name else "virt-mem " ^ name in
169 Options:") cmd summary description in
171 (* Now begin proper parsing of the command line arguments. *)
172 let debug = ref false in
173 let images = ref [] in
175 let anon_args = ref [] in
177 (* Default wordsize. *)
178 let def_wordsize = ref None in
179 let set_wordsize = function
180 | "32" -> def_wordsize := Some W32
181 | "64" -> def_wordsize := Some W64
182 | "auto" -> def_wordsize := None
183 | str -> failwith (sprintf (f_"set_wordsize: %s: unknown wordsize") str)
186 (* Default endianness. *)
187 let def_endian = ref None in
188 let set_endian = function
189 | "auto" -> def_endian := None
190 | "le" | "little" | "littleendian" | "intel" ->
191 def_endian := Some Bitmatch.LittleEndian
192 | "be" | "big" | "bigendian" | "motorola" ->
193 def_endian := Some Bitmatch.BigEndian
194 | str -> failwith (sprintf (f_"set_endian: %s: unknown endianness") str)
197 (* Default architecture. *)
198 let def_architecture = ref None in
199 let set_architecture = function
200 | "auto" -> def_architecture := None
202 let arch = architecture_of_string arch in
203 def_architecture := Some arch;
204 def_endian := Some (endian_of_architecture arch);
205 def_wordsize := Some (wordsize_of_architecture arch)
208 (* Default text address. *)
209 let def_text_addr = ref 0L (* 0 = auto-detect *) in
210 let set_text_addr = function
211 | "auto" -> def_text_addr := 0L
212 | "i386" -> def_text_addr := 0xc010_0000_L (* common for x86 *)
213 | "x86-64"|"x86_64" -> def_text_addr := 0xffffffff_81000000_L (* x86-64? *)
214 | str -> def_text_addr := Int64.of_string str
217 (* Handle -t option. *)
218 let memory_image filename =
220 (!def_wordsize, !def_endian, !def_architecture, !def_text_addr, filename)
224 (* Handle --version option. *)
226 printf "virt-mem %s\n" Virt_mem_version.version;
228 let major, minor, release =
229 let v, _ = Libvirt.get_version () in
230 v / 1_000_000, (v / 1_000) mod 1_000, v mod 1_000 in
231 printf "libvirt %d.%d.%d\n" major minor release;
235 (* Function to collect up any anonymous args (domain names/IDs). *)
236 let anon_arg str = anon_args := str :: !anon_args in
238 (* Construct the argspec.
239 * May include extra arguments specified by the tool.
242 let extra_args = match tool with
244 | Some (_, _, _, _, extra_args, _, _, _) -> extra_args in
246 "-A", Arg.String set_architecture,
247 "arch " ^ s_"Set kernel architecture, endianness and word size";
248 "-E", Arg.String set_endian,
249 "endian " ^ s_"Set kernel endianness";
250 "-T", Arg.String set_text_addr,
251 "addr " ^ s_"Set kernel text address";
252 "-W", Arg.String set_wordsize,
253 "addr " ^ s_"Set kernel word size";
254 "-c", Arg.Set_string uri,
255 "uri " ^ s_ "Connect to URI";
256 "--connect", Arg.Set_string uri,
257 "uri " ^ s_ "Connect to URI";
258 "--debug", Arg.Set debug,
259 " " ^ s_"Debug mode (default: false)";
260 "-t", Arg.String memory_image,
261 "image " ^ s_"Use saved kernel memory image";
262 "--version", Arg.Unit version,
263 " " ^ s_"Display version and exit";
266 (* Sort options alphabetically on first alpha character. *)
267 let cmp (a,_,_) (b,_,_) =
269 let a = String.strip ~chars a and b = String.strip ~chars b in
272 let argspec = List.sort ~cmp argspec in
273 (* Make the options line up nicely. *)
276 (* Parse the command line. This will exit if --version or --help found. *)
277 Arg.parse argspec anon_arg usage_msg;
279 let images = !images in
280 let debug = !debug in
281 let uri = if !uri = "" then None else Some !uri in
283 (* Discard the first anonymous argument if, above, we previously
284 * found it contained the tool name.
286 let anon_args = List.rev !anon_args in
288 if ignore_first_anon_arg then List.tl anon_args else anon_args in
290 (* At this point, either --help was specified on the command line
291 * (and so the program has exited) or we must have determined tool,
292 * or the user didn't give us a valid tool (eg. "virt-mem foobar").
293 * Detect that final case now and give an error.
295 let name, _, _, _, _, argcheck, beforeksyms, run =
300 virt-mem: I could not work out which tool you are trying to run.
301 Use 'virt-mem --help' for more help or read the manual page virt-mem(1)");
304 if debug then eprintf "tool = %s\n%!" name;
306 (* Optional argument checking in the tool. *)
309 | Some argcheck -> argcheck debug
312 (* Get the kernel images. *)
314 if images = [] then (
317 try C.connect_readonly ?name ()
318 with Libvirt.Virterror err ->
319 prerr_endline (Libvirt.Virterror.to_string err);
320 (* If non-root and no explicit connection URI, print a warning. *)
321 if Unix.geteuid () <> 0 && name = None then (
322 print_endline (s_ "NB: If you want to monitor a local Xen hypervisor, you usually need to be root");
326 (* If we have a list of parameters, then it is the domain names / UUIDs /
327 * IDs ONLY that we wish to display. Otherwise, display all active.
330 if anon_args = [] then (
331 (* List of active domains. *)
332 let nr_active_doms = C.num_of_domains conn in
334 Array.to_list (C.list_domains conn nr_active_doms) in
335 List.map (D.lookup_by_id conn) active_doms
340 try D.lookup_by_uuid_string conn arg
342 try D.lookup_by_name conn arg
344 try D.lookup_by_id conn (int_of_string arg)
346 failwith (sprintf (f_"%s: unknown domain (not a UUID, name or ID of any active domain)") arg) in
348 (* XXX Primitive test to see if the domain is active. *)
349 let is_active = try D.get_id dom >= 0 with _ -> false in
350 if not is_active then
351 failwith (sprintf (f_"%s: domain is not running") arg);
358 let xmls = List.map (fun dom -> dom, D.get_xml_desc dom) doms in
361 let xmls = List.map (fun (dom, xml) ->
362 dom, Xml.parse_string xml) xmls in
364 (* XXX Do something with the XML XXX
365 * such as detecting arch, wordsize, endianness.
375 let id = D.get_id dom in
376 let name = D.get_name dom in
379 match !def_wordsize with
382 (sprintf (f_"%s: use -W to define word size for this image")
386 match !def_endian with
389 (sprintf (f_"%s: use -E to define endianness for this image")
394 match !def_architecture with
395 | Some I386 -> I386 | Some X86_64 -> X86_64
398 (sprintf (f_"%s: use -A to define architecture (i386/x86-64 only) for this image") name) in
400 if !def_text_addr = 0L then
402 (sprintf (f_"%s: use -T to define kernel load address for this image") name);
404 let start_t = gettimeofday () in
406 (* Read the kernel memory.
407 * Maximum 64K can be read over remote connections.
409 let str = String.create kernel_size in
411 let remaining = kernel_size - i in
412 if remaining > 0 then (
413 let size = min remaining max_memory_peek in
414 D.memory_peek dom [D.Virtual]
415 (!def_text_addr +^ Int64.of_int i) size str i;
422 let end_t = gettimeofday () in
423 eprintf "timing: downloading kernel took %f seconds\n%!"
427 (* Map the virtual memory. *)
428 let mem = MMap.of_string str !def_text_addr in
430 (* Force the wordsize and endianness. *)
431 let mem = MMap.set_wordsize mem wordsize in
432 let mem = MMap.set_endian mem endian in
434 ((Some id, name, arch, mem) : image)
437 (* One or more -t options passed. *)
438 if anon_args <> [] then
439 failwith (s_"virt-mem: if -t given on command line, then no domain arguments should be listed");
442 fun (wordsize, endian, arch, text_addr, filename) ->
443 (* Quite a lot of limitations on the kernel images we can
444 * handle at the moment ...
446 (* XXX We could auto-detect wordsize easily. *)
451 (sprintf (f_"%s: use -W to define word size for this image")
458 (sprintf (f_"%s: use -E to define endianness for this image")
464 | Some I386 -> I386 | Some X86_64 -> X86_64
467 (sprintf (f_"%s: use -A to define architecture (i386/x86-64 only) for this image") filename) in
469 if text_addr = 0L then
471 (sprintf (f_"%s: use -T to define kernel load address for this image")
474 (* Map the virtual memory. *)
475 let fd = openfile filename [O_RDONLY] 0 in
476 let mem = MMap.of_file fd text_addr in
478 (* Force the wordsize and endianness. *)
479 let mem = MMap.set_wordsize mem wordsize in
480 let mem = MMap.set_endian mem endian in
482 ((None, filename, arch, mem) : image)
486 (* Optional callback into the tool before we start looking for
489 (match beforeksyms with
491 | Some beforeksyms -> beforeksyms debug images
494 (* If there is no run function, then there is no point continuing
495 * with the rest of the program (kernel symbol analysis) ...
497 if run = None then exit 0;
499 (* Now kernel symbol analysis starts ... *)
502 fun (domid, name, arch, mem) ->
503 (* Look for some common entries in the exported symbol table and
504 * from that find the symbol table itself. These are just
505 * supposed to be symbols which are very likely to be present
506 * in any Linux kernel, although we only need one of them to be
507 * present to find the symbol table.
509 * NB. Must not be __initdata, must be in EXPORT_SYMBOL.
512 "init_task"; (* first task_struct *)
513 "root_mountflags"; (* flags for mounting root fs *)
514 "init_uts_ns"; (* uname strings *)
515 "sys_open"; (* open(2) entry point *)
516 "sys_chdir"; (* chdir(2) entry point *)
517 "sys_chroot"; (* chroot(2) entry point *)
518 "sys_umask"; (* umask(2) entry point *)
519 "schedule"; (* scheduler entry point *)
521 (* Searching for <NUL>string<NUL> *)
522 let common_ksyms_nul = List.map (sprintf "\000%s\000") common_ksyms in
524 let start_t = gettimeofday () in
526 (* Search for these strings in the memory image. *)
527 let ksym_strings = List.map (MMap.find_all mem) common_ksyms_nul in
528 let ksym_strings = List.concat ksym_strings in
529 (* Adjust found addresses to start of the string (skip <NUL>). *)
530 let ksym_strings = List.map Int64.succ ksym_strings in
533 let end_t = gettimeofday () in
534 eprintf "timing: searching for common_ksyms took %f seconds\n%!"
538 let start_t = gettimeofday () in
540 (* For any we found, try to look up the symbol table
541 * base addr and size.
543 let ksymtabs = List.map (
545 (* Search for 'addr' appearing in the image. *)
546 let addrs = MMap.find_pointer_all mem addr in
548 (* Now consider each of these addresses and search back
549 * until we reach the beginning of the (possible) symbol
552 * Kernel symbol table struct is:
553 * struct kernel_symbol {
554 * unsigned long value;
555 * const char *name; <-- initial pointer
558 let pred_long2 addr =
559 MMap.pred_long mem (MMap.pred_long mem addr)
561 let base_addrs = List.map (
564 (* '*addr' should point to a C identifier. If it does,
565 * step backwards to the previous symbol table entry.
567 let addrp = MMap.follow_pointer mem addr in
568 if MMap.is_C_identifier mem addrp then
569 loop (pred_long2 addr)
571 MMap.succ_long mem addr
576 (* Also look for the end of the symbol table and
577 * calculate its size.
579 let base_addrs_sizes = List.map (
582 let addr2 = MMap.succ_long mem addr in
583 let addr2p = MMap.follow_pointer mem addr2 in
584 if MMap.is_C_identifier mem addr2p then
585 loop (MMap.succ_long mem addr2)
589 let end_addr = loop base_addr in
590 base_addr, end_addr -^ base_addr
595 let ksymtabs = List.concat ksymtabs in
597 (* Simply ignore any symbol table candidates which are too small. *)
598 let ksymtabs = List.filter (fun (_, size) -> size > 64L) ksymtabs in
601 eprintf "%s: candidate symbol tables at:\n" name;
604 eprintf "\t%Lx\t%Lx\t%!" addr size;
605 eprintf "first symbol: %s\n%!"
607 (MMap.follow_pointer mem
608 (MMap.succ_long mem addr)))
612 (* Vote for the most popular symbol table candidate and from this
613 * generate a function to look up ksyms.
616 let freqs = frequency ksymtabs in
619 eprintf (f_"%s: cannot find start of kernel symbol table\n") name;
620 (fun _ -> raise Not_found)
622 | (_, (ksymtab_addr, ksymtab_size)) :: _ ->
625 "%s: Kernel symbol table found at %Lx, size %Lx bytes\n%!"
626 name ksymtab_addr ksymtab_size;
628 (* Load the whole symbol table as a bitstring. *)
630 Bitmatch.bitstring_of_string
631 (MMap.get_bytes mem ksymtab_addr
632 (Int64.to_int ksymtab_size)) in
634 (* Function to look up an address in the symbol table. *)
635 let lookup_ksym sym =
636 let bits = bits_of_wordsize (MMap.get_wordsize mem) in
637 let e = MMap.get_endian mem in
640 | { value : bits : endian(e);
641 name_ptr : bits : endian(e) }
642 when MMap.get_string mem name_ptr = sym ->
644 | { _ : bits : endian(e);
645 _ : bits : endian(e);
646 bs : -1 : bitstring } ->
648 | { _ } -> raise Not_found
657 let end_t = gettimeofday () in
658 eprintf "timing: searching for ordinary ksyms took %f seconds\n%!"
662 let start_t = gettimeofday () in
664 (* Now try to find the /proc/kallsyms table. This is in an odd
665 * compressed format (but not a very successful compression
666 * format). However if it exists we know that it will contain
667 * addresses of the common ksyms above, and it has some
668 * characteristics which make it easy to detect in the
671 * kallsyms contains a complete list of symbols so is much
672 * more useful than the basic list of exports.
674 let ksym_addrs = List.filter_map (
675 fun ksym -> try Some (lookup_ksym ksym) with Not_found -> None
678 (* Search for those kernel addresses in the image. We're looking
679 * for the table kallsyms_addresses followed by kallsyms_num_syms
680 * (number of symbols in the table).
682 let ksym_addrs = List.map (MMap.find_pointer_all mem) ksym_addrs in
683 let ksym_addrs = List.concat ksym_addrs in
685 (* Test each one to see if it's a candidate list of kernel
686 * addresses followed by length of list.
688 let kallsymtabs = List.filter_map (
690 (* Search upwards from address until we find the length field.
691 * If found, jump backwards by length and check all addresses.
694 eprintf "%s: testing candidate kallsyms at %Lx\n" name addr;
696 let addrp = MMap.follow_pointer mem addr in
697 if MMap.is_mapped mem addrp then
698 loop (MMap.succ_long mem addr) (* continue up the table *)
700 if addrp >= min_kallsyms_tabsize &&
701 addrp <= max_kallsyms_tabsize then (
702 (* addrp might be the symbol count. Count backwards and
703 * check the full table.
705 let num_entries = Int64.to_int addrp in
706 let entry_size = bytes_of_wordsize (MMap.get_wordsize mem) in
708 addr -^ Int64.of_int (entry_size * num_entries) in
709 let end_addr = addr in
711 if addr < end_addr then (
712 let addrp = MMap.follow_pointer mem addr in
713 if MMap.is_mapped mem addrp then
714 loop2 (MMap.succ_long mem addr)
716 None (* can't verify the full address table *)
719 let names_addr = MMap.succ_long mem end_addr in
721 eprintf "%s: candidate kallsyms found at %Lx (names_addr at %Lx, num_entries %d)\n"
722 name start_addr names_addr num_entries;
723 Some (start_addr, num_entries, names_addr)
732 | Some (start_addr, num_entries, names_addr) ->
733 (* As an additional verification, check the list of
737 (* If the first byte is '\000' and is followed by a
738 * C identifier, then this is old-school list of
739 * symbols with prefix compression as in 2.6.9.
740 * Otherwise Huffman-compressed kallsyms as in
743 if MMap.get_byte mem names_addr = 0 &&
744 MMap.is_C_identifier mem (names_addr+^1L) then (
745 let names = ref [] in
747 let rec loop names_addr start_addr num =
749 let prefix = MMap.get_byte mem names_addr in
750 let prefix = String.sub !prev 0 prefix in
751 let name = MMap.get_string mem (names_addr+^1L) in
752 let len = String.length name in
753 let name = prefix ^ name in
755 let names_addr = names_addr +^ Int64.of_int len +^ 2L in
756 let sym_value = MMap.follow_pointer mem start_addr in
757 let start_addr = MMap.succ_long mem start_addr in
758 (*eprintf "%S -> %Lx\n" name sym_value;*)
759 names := (name, sym_value) :: !names;
760 loop names_addr start_addr (num-1)
763 loop names_addr start_addr num_entries;
764 let names = List.rev !names in
766 Some (start_addr, num_entries, names_addr,
769 else ( (* new-style "compressed" names. *)
770 let compressed_names = ref [] in
771 let rec loop names_addr start_addr num =
773 let len = MMap.get_byte mem names_addr in
774 let name = MMap.get_bytes mem (names_addr+^1L) len in
775 let names_addr = names_addr +^ Int64.of_int len +^ 1L in
776 let sym_value = MMap.follow_pointer mem start_addr in
777 let start_addr = MMap.succ_long mem start_addr in
779 (name, sym_value) :: !compressed_names;
780 loop names_addr start_addr (num-1)
784 let markers_addr = loop names_addr start_addr num_entries in
785 let markers_addr = MMap.align mem markers_addr in
786 let compressed_names = List.rev !compressed_names in
788 Some (start_addr, num_entries, names_addr,
789 Compressed (compressed_names, markers_addr))
792 Invalid_argument _ -> None (* bad names list *)
796 eprintf "%s: candidate kallsyms at:\n" name;
799 | (start_addr, num_entries, names_addr, Uncompressed _) ->
800 eprintf "\t%Lx %d entries names_addr=%Lx old-style\n%!"
801 start_addr num_entries names_addr
802 | (start_addr, num_entries, names_addr,
803 Compressed (_, markers_addr)) ->
804 eprintf "\t%Lx %d entries names_addr=%Lx markers_addr=%Lx\n%!"
805 start_addr num_entries names_addr markers_addr
809 (* Vote for the most popular symbol table candidate and
810 * enhance the function for looking up ksyms.
813 let freqs = frequency kallsymtabs in
816 (* Can't find any kallsymtabs, just return the lookup_ksym
817 * function generated previously from the exported symbols.
821 | (_, (_, _, _, Uncompressed names)) :: _ ->
822 let lookup_ksym name =
823 try (* first look it up in kallsyms table. *)
824 List.assoc name names
825 with Not_found -> (* try the old exports table instead *)
830 | (_, (start_addr, num_entries, names_addr,
831 Compressed (compressed_names, markers_addr))) :: _ ->
832 (* Skip the markers and look for the token table. *)
833 let num_markers = Int64.of_int ((num_entries + 255) / 256) in
835 Int64.of_int (bytes_of_wordsize (MMap.get_wordsize mem)) in
836 let tokens_addr = markers_addr +^ marker_size *^ num_markers in
838 (* Now read out the compression tokens, which are just
839 * 256 ASCIIZ strings that map bytes in the compression
840 * names to substrings.
842 let tokens = Array.make 256 "" in
843 let rec loop i addr =
845 let str = MMap.get_string mem addr in
846 let len = String.length str in
847 let addr = addr +^ Int64.of_int (len+1) in
854 (* Expand the compressed names using the tokens. *)
855 let names = List.filter_map (
856 fun (name, sym_value) ->
857 let f c = tokens.(Char.code c) in
858 let name = String.replace_chars f name in
859 (* First character in uncompressed output is the symbol
860 * type, eg. 'T'/'t' for text etc.
862 (* NOTE: Symbol names are NOT unique
863 * (eg. 'con_start' is both a function and data in
864 * some kernels). XXX We need to handle this situation
867 (*let typ = name.[0] in*)
868 let name = String.sub name 1 (String.length name - 1) in
869 (*eprintf "%S -> %Lx\n" name sym_value;*)
870 Some (name, sym_value)
871 ) compressed_names in
873 let lookup_ksym name =
874 try (* first look it up in kallsyms table. *)
875 List.assoc name names
876 with Not_found -> (* try the old exports table instead *)
883 let end_t = gettimeofday () in
884 eprintf "timing: searching for kallsyms took %f seconds\n%!"
888 (* Just wrap the lookup_ksym call in something which prints
889 * the query when debug is set.
893 let lookup_ksym sym =
895 let value = lookup_ksym sym in
896 eprintf "lookup_ksym %S = %Lx\n%!" sym value;
899 eprintf "lookup_ksym %S failed\n%!" sym;
907 ((domid, name, arch, mem, lookup_ksym) : image_with_ksyms)
910 (* Run the tool's main function. *)