diff agent/src/share/native/sadis.c @ 6782:5a98bf7d847b

6879063: SA should use hsdis for disassembly Summary: We should in SA to use hsdis for it like the JVM does to replace the current java based disassembler. Reviewed-by: twisti, jrose, sla Contributed-by: yumin.qi@oracle.com
author minqi
date Mon, 24 Sep 2012 12:44:00 -0700
parents
children 7a40901e0d5c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/share/native/sadis.c	Mon Sep 24 12:44:00 2012 -0700
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2012, Oracle and/or its affiliates. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "sun_jvm_hotspot_asm_Disassembler.h"
+
+/*
+ *  This file implements a binding between Java and the hsdis
+ *  dissasembler.  It should compile on Linux/Solaris and Windows.
+ *  The only platform dependent pieces of the code for doing
+ *  dlopen/dlsym to find the entry point in hsdis.  All the rest is
+ *  standard JNI code.
+ */
+
+#ifdef _WINDOWS
+
+#define snprintf  _snprintf
+#define vsnprintf _vsnprintf
+
+#include <windows.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+
+#else
+
+#include <strings.h>
+#include <dlfcn.h>
+#include <link.h>
+
+#endif
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef _WINDOWS
+static int getLastErrorString(char *buf, size_t len)
+{
+    long errval;
+
+    if ((errval = GetLastError()) != 0)
+    {
+      /* DOS error */
+      size_t n = (size_t)FormatMessage(
+            FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+            NULL,
+            errval,
+            0,
+            buf,
+            (DWORD)len,
+            NULL);
+      if (n > 3) {
+        /* Drop final '.', CR, LF */
+        if (buf[n - 1] == '\n') n--;
+        if (buf[n - 1] == '\r') n--;
+        if (buf[n - 1] == '.') n--;
+        buf[n] = '\0';
+      }
+      return (int)n;
+    }
+
+    if (errno != 0)
+    {
+      /* C runtime error that has no corresponding DOS error code */
+      const char *s = strerror(errno);
+      size_t n = strlen(s);
+      if (n >= len) n = len - 1;
+      strncpy(buf, s, n);
+      buf[n] = '\0';
+      return (int)n;
+    }
+    return 0;
+}
+#endif /* _WINDOWS */
+
+/*
+ * Class:     sun_jvm_hotspot_asm_Disassembler
+ * Method:    load_library
+ * Signature: (Ljava/lang/String;)L
+ */
+JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_asm_Disassembler_load_1library(JNIEnv * env,
+                                                                           jclass disclass,
+                                                                           jstring jrepath_s,
+                                                                           jstring libname_s) {
+  uintptr_t func = 0;
+  const char* error_message = NULL;
+  const char* java_home;
+  jboolean isCopy;
+  uintptr_t *handle = NULL;
+
+  const char * jrepath = (*env)->GetStringUTFChars(env, jrepath_s, &isCopy); // like $JAVA_HOME/jre/lib/sparc/
+  const char * libname = (*env)->GetStringUTFChars(env, libname_s, &isCopy);
+  char buffer[128];
+
+  /* Load the hsdis library */
+#ifdef _WINDOWS
+  HINSTANCE hsdis_handle;
+  hsdis_handle = LoadLibrary(libname);
+  if (hsdis_handle == NULL) {
+    snprintf(buffer, sizeof(buffer), "%s%s", jrepath, libname);
+    hsdis_handle = LoadLibrary(buffer);
+  }
+  if (hsdis_handle != NULL) {
+    func = (uintptr_t)GetProcAddress(hsdis_handle, "decode_instructions_virtual");
+  }
+  if (func == 0) {
+    getLastErrorString(buffer, sizeof(buffer));
+    error_message = buffer;
+  }
+#else
+  void* hsdis_handle;
+  hsdis_handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL);
+  if (hsdis_handle == NULL) {
+    snprintf(buffer, sizeof(buffer), "%s%s", jrepath, libname);
+    hsdis_handle = dlopen(buffer, RTLD_LAZY | RTLD_GLOBAL);
+  }
+  if (hsdis_handle != NULL) {
+    func = (uintptr_t)dlsym(hsdis_handle, "decode_instructions_virtual");
+  }
+  if (func == 0) {
+    error_message = dlerror();
+  }
+#endif
+
+  (*env)->ReleaseStringUTFChars(env, libname_s, libname);
+  (*env)->ReleaseStringUTFChars(env, jrepath_s, jrepath);
+
+  if (func == 0) {
+    /* Couldn't find entry point.  error_message should contain some
+     * platform dependent error message.
+     */
+    jclass eclass = (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException");
+    (*env)->ThrowNew(env, eclass, error_message);
+  }
+  return (jlong)func;
+}
+
+/* signature of decode_instructions_virtual from hsdis.h */
+typedef void* (*decode_func)(uintptr_t start_va, uintptr_t end_va,
+                             unsigned char* start, uintptr_t length,
+                             void* (*event_callback)(void*, const char*, void*),
+                             void* event_stream,
+                             int (*printf_callback)(void*, const char*, ...),
+                             void* printf_stream,
+                             const char* options);
+
+/* container for call back state when decoding instructions */
+typedef struct {
+  JNIEnv* env;
+  jobject dis;
+  jobject visitor;
+  jmethodID handle_event;
+  jmethodID raw_print;
+  char buffer[4096];
+} decode_env;
+
+
+/* event callback binding to Disassembler.handleEvent */
+static void* event_to_env(void* env_pv, const char* event, void* arg) {
+  decode_env* denv = (decode_env*)env_pv;
+  JNIEnv* env = denv->env;
+  jstring event_string = (*env)->NewStringUTF(env, event);
+  jlong result = (*env)->CallLongMethod(env, denv->dis, denv->handle_event, denv->visitor,
+                                        event_string, (jlong) (uintptr_t)arg);
+  if ((*env)->ExceptionOccurred(env) != NULL) {
+    /* ignore exceptions for now */
+    (*env)->ExceptionClear(env);
+    result = 0;
+  }
+  return (void*)(uintptr_t)result;
+}
+
+/* printing callback binding to Disassembler.rawPrint */
+static int printf_to_env(void* env_pv, const char* format, ...) {
+  jstring output;
+  va_list ap;
+  int cnt;
+  decode_env* denv = (decode_env*)env_pv;
+  JNIEnv* env = denv->env;
+  size_t flen = strlen(format);
+  const char* raw = NULL;
+
+  if (flen == 0)  return 0;
+  if (flen < 2 ||
+      strchr(format, '%') == NULL) {
+    raw = format;
+  } else if (format[0] == '%' && format[1] == '%' &&
+             strchr(format+2, '%') == NULL) {
+    // happens a lot on machines with names like %foo
+    flen--;
+    raw = format+1;
+  }
+  if (raw != NULL) {
+    jstring output = (*env)->NewStringUTF(env, raw);
+    (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output);
+    if ((*env)->ExceptionOccurred(env) != NULL) {
+      /* ignore exceptions for now */
+      (*env)->ExceptionClear(env);
+    }
+    return (int) flen;
+  }
+  va_start(ap, format);
+  cnt = vsnprintf(denv->buffer, sizeof(denv->buffer), format, ap);
+  va_end(ap);
+
+  output = (*env)->NewStringUTF(env, denv->buffer);
+  (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output);
+  if ((*env)->ExceptionOccurred(env) != NULL) {
+    /* ignore exceptions for now */
+    (*env)->ExceptionClear(env);
+  }
+  return cnt;
+}
+
+/*
+ * Class:     sun_jvm_hotspot_asm_Disassembler
+ * Method:    decode
+ * Signature: (Lsun/jvm/hotspot/asm/InstructionVisitor;J[BLjava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL Java_sun_jvm_hotspot_asm_Disassembler_decode(JNIEnv * env,
+                                                                    jobject dis,
+                                                                    jobject visitor,
+                                                                    jlong startPc,
+                                                                    jbyteArray code,
+                                                                    jstring options_s,
+                                                                    jlong decode_instructions_virtual) {
+  jboolean isCopy;
+  jbyte* start = (*env)->GetByteArrayElements(env, code, &isCopy);
+  jbyte* end = start + (*env)->GetArrayLength(env, code);
+  const char * options = (*env)->GetStringUTFChars(env, options_s, &isCopy);
+  jclass disclass = (*env)->GetObjectClass(env, dis);
+
+  decode_env denv;
+  denv.env = env;
+  denv.dis = dis;
+  denv.visitor = visitor;
+
+  /* find Disassembler.handleEvent callback */
+  denv.handle_event = (*env)->GetMethodID(env, disclass, "handleEvent",
+                                          "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;J)J");
+  if ((*env)->ExceptionOccurred(env)) {
+    return;
+  }
+
+  /* find Disassembler.rawPrint callback */
+  denv.raw_print = (*env)->GetMethodID(env, disclass, "rawPrint",
+                                       "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;)V");
+  if ((*env)->ExceptionOccurred(env)) {
+    return;
+  }
+
+  /* decode the buffer */
+  (*(decode_func)(uintptr_t)decode_instructions_virtual)(startPc,
+                                                         startPc + end - start,
+                                                         (unsigned char*)start,
+                                                         end - start,
+                                                         &event_to_env,  (void*) &denv,
+                                                         &printf_to_env, (void*) &denv,
+                                                         options);
+
+  /* cleanup */
+  (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT);
+  (*env)->ReleaseStringUTFChars(env, options_s, options);
+}