Version 0.2.0.
[virt-dmesg.git] / src / c_utils.c
diff --git a/src/c_utils.c b/src/c_utils.c
new file mode 100644 (file)
index 0000000..d413ff8
--- /dev/null
@@ -0,0 +1,330 @@
+/* virt-dmesg
+ * (C) Copyright 2008-2011 Red Hat Inc.
+ *
+ * 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 functions written in C.  We can drop into C wherever
+ * we need to do something that would be much faster than OCaml or
+ * where it's just awkward to do something in OCaml or much easier in
+ * C.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+#include <endian.h>
+
+#include <zlib.h>
+
+#include <caml/alloc.h>
+#include <caml/fail.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+
+/* This vital function returns the offset of the virtual address
+ * relative to the base address.  If the virtual address is outside
+ * the kernel, then it returns -1.
+ *
+ * Note that the function is declared "noalloc" for speed.  It must
+ * not allocate anything on the OCaml heap.
+ *
+ * str_mapping : int64 -> int64 -> int -> int
+ */
+value
+virt_dmesg_str_mapping (value base_addrv, value addrv, value str_lenv)
+{
+  uint64_t base_addr = Int64_val (base_addrv);
+  uint64_t addr = Int64_val (addrv);
+  int str_len = Int_val (str_lenv);
+  uint64_t off;
+
+  off = addr - base_addr;
+  if (off < str_len)
+    return Val_int (off);
+  else
+    return Val_int (-1);
+}
+
+/* NOTE: Declared as a "noalloc" function for speed. */
+value
+virt_dmesg_addr_compare (value a1v, value a2v)
+{
+  uint64_t a1 = Int64_val (a1v);
+  uint64_t a2 = Int64_val (a2v);
+
+  if (a1 < a2)
+    return (Val_int (-1));
+  else if (a1 > a2)
+    return (Val_int (1));
+  else
+    return (Val_int (0));
+}
+
+/* Find 'needle' in 'haystack', but start searching haystack from 'off'.
+ *
+ * strstr_from : string -> string -> int -> int
+ */
+value
+virt_dmesg_strstr_from (value haystackv, value needlev, value offv)
+{
+  CAMLparam3 (haystackv, needlev, offv);
+  const char *haystack = String_val (haystackv);
+  int off = Int_val (offv);
+  const char *r;
+  ptrdiff_t i;
+
+  /* Because OCaml strings can contain '\0' characters, use memmem
+   * not strstr.
+   */
+  r = memmem (haystack + off, caml_string_length (haystackv) - off,
+              String_val (needlev), caml_string_length (needlev));
+  if (r == 0)
+    caml_raise_not_found ();
+
+  i = r - haystack;
+  CAMLreturn (Val_int (i));
+}
+
+/* This is like 'strstr_from' but it only tests for aligned 'needle',
+ * eg. if 'needle' was 4 bytes long then it would only look every 4
+ * bytes.
+ */
+value
+virt_dmesg_strstr_from_aligned (value haystackv, value needlev, value offv)
+{
+  CAMLparam3 (haystackv, needlev, offv);
+  const char *haystack = String_val (haystackv);
+  size_t haystack_len = caml_string_length (haystackv);
+  const char *needle = String_val (needlev);
+  size_t needle_len = caml_string_length (needlev);
+  int off = Int_val (offv);
+  int r;
+
+  for (r = off; r < haystack_len; r += needle_len) {
+    if (memcmp (haystack + r, needle, needle_len) == 0)
+      goto found;
+  }
+  caml_raise_not_found ();
+
+ found:
+  CAMLreturn (Val_int (r));
+}
+
+/* Extract endian 32 and 64 bit integers (or pointers) from strings. */
+value
+virt_dmesg_str_get_le32 (value strv, value offv)
+{
+  CAMLparam2 (strv, offv);
+  CAMLlocal1 (rv);
+  const char *str = String_val (strv);
+  int off = Int_val (offv);
+  uint64_t r;
+
+  r = le32toh (*(const uint32_t *)(str + off));
+  rv = caml_copy_int64 (r);
+
+  CAMLreturn (rv);
+}
+
+value
+virt_dmesg_str_get_be32 (value strv, value offv)
+{
+  CAMLparam2 (strv, offv);
+  CAMLlocal1 (rv);
+  const char *str = String_val (strv);
+  int off = Int_val (offv);
+  uint64_t r;
+
+  r = be32toh (*(const uint32_t *)(str + off));
+  rv = caml_copy_int64 (r);
+
+  CAMLreturn (rv);
+}
+
+value
+virt_dmesg_str_get_le64 (value strv, value offv)
+{
+  CAMLparam2 (strv, offv);
+  CAMLlocal1 (rv);
+  const char *str = String_val (strv);
+  int off = Int_val (offv);
+  uint64_t r;
+
+  r = le64toh (*(const uint64_t *)(str + off));
+  rv = caml_copy_int64 (r);
+
+  CAMLreturn (rv);
+}
+
+value
+virt_dmesg_str_get_be64 (value strv, value offv)
+{
+  CAMLparam2 (strv, offv);
+  CAMLlocal1 (rv);
+  const char *str = String_val (strv);
+  int off = Int_val (offv);
+  uint64_t r;
+
+  r = be64toh (*(const uint64_t *)(str + off));
+  rv = caml_copy_int64 (r);
+
+  CAMLreturn (rv);
+}
+
+/* Convert integers of various endianness/word size to strings of bytes.
+ *
+ * str_of_*32 returns a 4 byte string
+ * str_of_*64 returns an 8 byte string
+ */
+value
+virt_dmesg_str_of_le32 (value addrv)
+{
+  CAMLparam1 (addrv);
+  CAMLlocal1 (rv);
+  const char *str;
+
+  rv = caml_alloc_string (4);
+  str = String_val (rv);
+  *(uint32_t *)str = htole32 (Int64_val (addrv));
+
+  CAMLreturn (rv);
+}
+
+value
+virt_dmesg_str_of_be32 (value addrv)
+{
+  CAMLparam1 (addrv);
+  CAMLlocal1 (rv);
+  const char *str;
+
+  rv = caml_alloc_string (4);
+  str = String_val (rv);
+  *(uint32_t *)str = htobe32 (Int64_val (addrv));
+
+  CAMLreturn (rv);
+}
+
+value
+virt_dmesg_str_of_le64 (value addrv)
+{
+  CAMLparam1 (addrv);
+  CAMLlocal1 (rv);
+  const char *str;
+
+  rv = caml_alloc_string (8);
+  str = String_val (rv);
+  *(uint64_t *)str = htole64 (Int64_val (addrv));
+
+  CAMLreturn (rv);
+}
+
+value
+virt_dmesg_str_of_be64 (value addrv)
+{
+  CAMLparam1 (addrv);
+  CAMLlocal1 (rv);
+  const char *str;
+
+  rv = caml_alloc_string (8);
+  str = String_val (rv);
+  *(uint64_t *)str = htobe64 (Int64_val (addrv));
+
+  CAMLreturn (rv);
+}
+
+/* Get NUL-terminated (ASCIIZ) string at the given offset.
+ *
+ * get_asciiz : string -> int -> string
+ */
+value
+virt_dmesg_get_asciiz (value strv, value offv)
+{
+  CAMLparam2 (strv, offv);
+  CAMLlocal1 (rv);
+  const char *str = String_val (strv);
+  size_t len = caml_string_length (strv);
+  int off = Int_val (offv);
+  size_t rlen;
+
+  /* Work out the length of the return value. */
+  for (rlen = 0; off+rlen < len; ++rlen)
+    if (str[off+rlen] == '\0')
+      break;
+
+  rv = caml_alloc_string (rlen);
+  /* Previous allocation could have moved the input string, so ...  */
+  str = String_val (strv);
+
+  memcpy (String_val (rv), &str[off], rlen);
+
+  CAMLreturn (rv);
+}
+
+/* Return true iff what is at the offset is plausibly a C programming
+ * language identifier.
+ *
+ * is_C_ident : string -> int -> bool
+ */
+value
+virt_dmesg_is_C_ident (value strv, value offv)
+{
+  CAMLparam2 (strv, offv);
+  const char *str = String_val (strv);
+  size_t len = caml_string_length (strv);
+  int off = Int_val (offv);
+  size_t i;
+  int r = 1 /* true */;
+
+  if (str[off] != '_' && !(str[off] >= 'A' && str[off] <= 'Z' ||
+                           str[off] >= 'a' && str[off] <= 'z')) {
+    r = 0 /* false */;
+    goto out;
+  }
+
+  for (i = 1; off+i < len; ++i) {
+    if (str[off+i] == '\0')
+      break;
+
+    if (str[off+i] != '_' && !(str[off+i] >= 'A' && str[off+i] <= 'Z' ||
+                               str[off+i] >= 'a' && str[off+i] <= 'z' ||
+                               str[off+i] >= '0' && str[off+i] <= '9')) {
+      r = 0 /* false */;
+      goto out;
+    }
+  }
+
+ out:
+  CAMLreturn (Val_int (r));
+}
+
+/* external crc32_of_string : string -> int32 */
+value
+virt_dmesg_crc32_of_string (value strv)
+{
+  CAMLparam1 (strv);
+  CAMLlocal1 (rv);
+  uLong r;
+
+  r = crc32 (0, Z_NULL, 0);
+  r = crc32 (r, String_val (strv), caml_string_length (strv));
+
+  rv = caml_copy_int32 (r);
+  CAMLreturn (rv);
+}