From: Richard W.M. Jones <"Richard W.M. Jones "> Date: Wed, 9 Jul 2008 15:22:25 +0000 (+0100) Subject: Rewrite critical search inner loop in C for speed. X-Git-Url: http://git.annexia.org/?p=virt-mem.git;a=commitdiff_plain;h=c6362265f3247c19a9791e2bae65e2c6747ecdf7 Rewrite critical search inner loop in C for speed. --- diff --git a/.hgignore b/.hgignore index 771c749..8824d7d 100644 --- a/.hgignore +++ b/.hgignore @@ -27,4 +27,5 @@ po/*.po.orig uname/virt-uname dmesg/virt-dmesg ps/virt-ps -samples \ No newline at end of file +samples +lib/*.so diff --git a/configure.ac b/configure.ac index 53287c4..ce6a7dd 100644 --- a/configure.ac +++ b/configure.ac @@ -22,6 +22,15 @@ AC_INIT(virt-mem,0.2.4) AC_PROG_INSTALL AC_PROG_MKDIR_P +dnl Check for an ANSI C compiler. +AC_GNU_SOURCE +AC_PROG_CC +AC_C_PROTOTYPES +test "x$U" != "x" && AC_MSG_ERROR(Compiler not ANSI compliant) +AC_PROG_CC_C_O + +AC_CHECK_FUNCS([memmem]) + dnl Check for basic OCaml environment & findlib. AC_PROG_OCAML AC_PROG_FINDLIB diff --git a/lib/Makefile.in b/lib/Makefile.in index f0c75f4..0a757e8 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -21,6 +21,11 @@ VERSION = @PACKAGE_VERSION@ INSTALL = @INSTALL@ +OCAMLLIB = @OCAMLLIB@ + +CC = @CC@ +CFLAGS = @CFLAGS@ -Wall -Werror -I$(OCAMLLIB) -I@top_srcdir@ + SYNTAX = -syntax bitmatch.syntax OCAMLCPACKAGES = -package unix,bigarray,extlib,libvirt,xml-light,bitmatch.syntax @@ -45,6 +50,7 @@ TARGETS = virt_mem.cma virt_mem.cmxa OBJS = virt_mem_gettext.cmo \ virt_mem_version.cmo \ virt_mem_utils.cmo \ + virt_mem_mmap_c.o \ virt_mem_mmap.cmo \ virt_mem.cmo XOBJS = $(OBJS:%.cmo=%.cmx) @@ -52,10 +58,10 @@ XOBJS = $(OBJS:%.cmo=%.cmx) all: $(TARGETS) virt_mem.cma: $(OBJS) - ocamlfind ocamlc $(OCAMLCFLAGS) $(OCAMLCPACKAGES) -a -o $@ $^ + ocamlmklib -o virt_mem $^ virt_mem.cmxa: $(XOBJS) - ocamlfind ocamlopt $(OCAMLOPTFLAGS) $(OCAMLOPTPACKAGES) -a -o $@ $^ + ocamlmklib -o virt_mem $^ install: diff --git a/lib/virt_mem.ml b/lib/virt_mem.ml index 384a089..1445633 100644 --- a/lib/virt_mem.ml +++ b/lib/virt_mem.ml @@ -236,8 +236,9 @@ let start usage_msg = if !def_text_addr = 0L then failwith - (sprintf (f_"%s: use -T to define kernel load address for this image") - name); + (sprintf (f_"%s: use -T to define kernel load address for this image") name); + + let start_t = gettimeofday () in (* Read the kernel memory. * Maximum 64K can be read over remote connections. @@ -254,6 +255,12 @@ let start usage_msg = in loop 0; + if debug then ( + let end_t = gettimeofday () in + eprintf "timing: downloading kernel took %f seconds\n%!" + (end_t -. start_t) + ); + (* Map the virtual memory. *) let mem = MMap.of_string str !def_text_addr in @@ -337,12 +344,22 @@ let start usage_msg = (* Searching for string *) let common_ksyms_nul = List.map (sprintf "\000%s\000") common_ksyms in + let start_t = gettimeofday () in + (* Search for these strings in the memory image. *) let ksym_strings = List.map (MMap.find_all mem) common_ksyms_nul in let ksym_strings = List.concat ksym_strings in (* Adjust found addresses to start of the string (skip ). *) let ksym_strings = List.map Int64.succ ksym_strings in + if debug then ( + let end_t = gettimeofday () in + eprintf "timing: searching for common_ksyms took %f seconds\n%!" + (end_t -. start_t) + ); + + let start_t = gettimeofday () in + (* For any we found, try to look up the symbol table * base addr and size. *) @@ -404,11 +421,11 @@ let start usage_msg = let ksymtabs = List.filter (fun (_, size) -> size > 64L) ksymtabs in if debug then ( - printf "%s: candidate symbol tables at:\n" name; + eprintf "%s: candidate symbol tables at:\n" name; List.iter ( fun (addr, size) -> - printf "\t%Lx\t%Lx\t%!" addr size; - printf "first symbol: %s\n%!" + eprintf "\t%Lx\t%Lx\t%!" addr size; + eprintf "first symbol: %s\n%!" (MMap.get_string mem (MMap.follow_pointer mem (MMap.succ_long mem addr))) @@ -427,7 +444,7 @@ let start usage_msg = | (_, (ksymtab_addr, ksymtab_size)) :: _ -> if debug then - printf + eprintf "%s: Kernel symbol table found at %Lx, size %Lx bytes\n%!" name ksymtab_addr ksymtab_size; @@ -459,6 +476,14 @@ let start usage_msg = lookup_ksym in + if debug then ( + let end_t = gettimeofday () in + eprintf "timing: searching for ordinary ksyms took %f seconds\n%!" + (end_t -. start_t) + ); + + let start_t = gettimeofday () in + (* Now try to find the /proc/kallsyms table. This is in an odd * compressed format (but not a very successful compression * format). However if it exists we know that it will contain @@ -489,7 +514,7 @@ let start usage_msg = * If found, jump backwards by length and check all addresses. *) if debug then - printf "%s: testing candidate kallsyms at %Lx\n" name addr; + eprintf "%s: testing candidate kallsyms at %Lx\n" name addr; let rec loop addr = let addrp = MMap.follow_pointer mem addr in if MMap.is_mapped mem addrp then @@ -516,7 +541,7 @@ let start usage_msg = (* ok! *) let names_addr = MMap.succ_long mem end_addr in if debug then - printf "%s: candidate kallsyms found at %Lx (names_addr at %Lx, num_entries %d)\n" + eprintf "%s: candidate kallsyms found at %Lx (names_addr at %Lx, num_entries %d)\n" name start_addr names_addr num_entries; Some (start_addr, num_entries, names_addr) in @@ -553,7 +578,7 @@ let start usage_msg = let names_addr = names_addr +^ Int64.of_int len +^ 2L in let sym_value = MMap.follow_pointer mem start_addr in let start_addr = MMap.succ_long mem start_addr in - (*printf "%S -> %Lx\n" name sym_value;*) + (*eprintf "%S -> %Lx\n" name sym_value;*) names := (name, sym_value) :: !names; loop names_addr start_addr (num-1) ) @@ -591,15 +616,15 @@ let start usage_msg = ) ksym_addrs in if debug then ( - printf "%s: candidate kallsyms at:\n" name; + eprintf "%s: candidate kallsyms at:\n" name; List.iter ( function | (start_addr, num_entries, names_addr, Uncompressed _) -> - printf "\t%Lx %d entries names_addr=%Lx old-style\n%!" + eprintf "\t%Lx %d entries names_addr=%Lx old-style\n%!" start_addr num_entries names_addr | (start_addr, num_entries, names_addr, Compressed (_, markers_addr)) -> - printf "\t%Lx %d entries names_addr=%Lx markers_addr=%Lx\n%!" + eprintf "\t%Lx %d entries names_addr=%Lx markers_addr=%Lx\n%!" start_addr num_entries names_addr markers_addr ) kallsymtabs ); @@ -664,7 +689,7 @@ let start usage_msg = *) (*let typ = name.[0] in*) let name = String.sub name 1 (String.length name - 1) in - (*printf "%S -> %Lx\n" name sym_value;*) + (*eprintf "%S -> %Lx\n" name sym_value;*) Some (name, sym_value) ) compressed_names in @@ -677,6 +702,12 @@ let start usage_msg = lookup_ksym in + if debug then ( + let end_t = gettimeofday () in + eprintf "timing: searching for kallsyms took %f seconds\n%!" + (end_t -. start_t) + ); + (* Just wrap the lookup_ksym call in something which prints * the query when debug is set. *) @@ -685,10 +716,10 @@ let start usage_msg = let lookup_ksym sym = try let value = lookup_ksym sym in - printf "lookup_ksym %S = %Lx\n%!" sym value; + eprintf "lookup_ksym %S = %Lx\n%!" sym value; value with Not_found -> - printf "lookup_ksym %S failed\n%!" sym; + eprintf "lookup_ksym %S failed\n%!" sym; raise Not_found in lookup_ksym diff --git a/lib/virt_mem_mmap.ml b/lib/virt_mem_mmap.ml index 98dc84a..963e10d 100644 --- a/lib/virt_mem_mmap.ml +++ b/lib/virt_mem_mmap.ml @@ -82,7 +82,7 @@ let add_string ({ mappings = mappings } as t) str addr = if addr &^ 7L <> 0L then invalid_arg "add_file: mapping address must be aligned to 8 bytes"; let size = String.length str in - (* Use Bigarray.Array1. XXX We should just use the string. *) + (* Copy the string data to a Bigarray. *) let arr = Array1.create char c_layout size in for i = 0 to size-1 do Array1.set arr i (String.unsafe_get str i) @@ -108,6 +108,15 @@ let _find_map { mappings = mappings } pred = in loop mappings +(* The following functions are actually written in C + * because memmem(3) is likely to be much faster than anything + * we could write in OCaml. + * + * Also OCaml bigarrays are specifically designed to be accessed + * easily from C: + * http://caml.inria.fr/pub/docs/manual-ocaml/manual043.html + *) +(* (* Array+offset = string? *) let string_at arr offset str strlen = let j = ref offset in @@ -144,6 +153,10 @@ let _find_in start align str arr = loop () ) else Some start +*) +external _find_in : + int -> int -> string -> (char,int8_unsigned_elt,c_layout) Array1.t -> + int option = "virt_mem_mmap_find_in" (* Generic find function. *) let _find t start align str = diff --git a/lib/virt_mem_mmap_c.c b/lib/virt_mem_mmap_c.c new file mode 100644 index 0000000..29dca72 --- /dev/null +++ b/lib/virt_mem_mmap_c.c @@ -0,0 +1,77 @@ +/* Memory info command for virtual domains. + (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc. + http://libvirt.org/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This contains hand-coded C functions written for speed. +*/ + +#include + +#include + +#include +#include +#include +#include +#include + +CAMLprim value +virt_mem_mmap_find_in (value startv, value alignv, value strv, value arrv) +{ + CAMLparam4 (startv, alignv, strv, arrv); + CAMLlocal2 (rv, vv); + long start = Long_val (startv); /* Start offset for search. */ + long align = Long_val (alignv); /* Alignment for search. */ + char *str = String_val (strv); /* String to search for. */ + int strlen = caml_string_length (strv); + void *data = Data_bigarray_val (arrv); /* Data in bigarray. */ + int datalen = Bigarray_val(arrv)->dim[0]; /* Total length of bigarray. */ + long i, r = -1; + void *p; + + /* memmem is a GNU extension. Note that we cannot use strstr because + * OCaml strings may contain NULs, and in this case it is very likely + * that it does contain NULs. + */ +#ifdef HAVE_MEMMEM + if (align == 1) { + p = memmem (data+start, datalen-start, str, strlen); + r = p ? p-data : -1; + goto ret; + } +#endif + + p = data+start; + for (i = start; i < datalen-strlen; i += align) { + if (memcmp (p, str, strlen) == 0) { + r = p-data; + goto ret; + } + p += align; + } + + ret: + if (r == -1) + rv = Val_int (0); /* None */ + else { + vv = Val_long (r); /* Some r */ + rv = caml_alloc (1, 0); + Store_field (rv, 0, vv); + } + + CAMLreturn (rv); +}