1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  *
  23  */
  24 
  25 #include "sun_jvm_hotspot_asm_Disassembler.h"
  26 
  27 #ifdef _WINDOWS
  28 
  29 #define snprintf  _snprintf
  30 #define vsnprintf _vsnprintf
  31 
  32 #include <windows.h>
  33 #include <sys/types.h>
  34 #include <sys/stat.h>
  35 #ifdef _DEBUG
  36 #include <crtdbg.h>
  37 #endif
  38 
  39 #else
  40 
  41 #include <strings.h>
  42 #include <dlfcn.h>
  43 #include <link.h>
  44 
  45 #endif
  46 
  47 #include <limits.h>
  48 #include <stdio.h>
  49 #include <stdarg.h>
  50 #include <stdlib.h>
  51 #include <errno.h>
  52 
  53 #ifdef _WINDOWS
  54 static int getLastErrorString(char *buf, size_t len)
  55 {
  56     long errval;
  57 
  58     if ((errval = GetLastError()) != 0)
  59     {
  60       /* DOS error */
  61       size_t n = (size_t)FormatMessage(
  62             FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
  63             NULL,
  64             errval,
  65             0,
  66             buf,
  67             (DWORD)len,
  68             NULL);
  69       if (n > 3) {
  70         /* Drop final '.', CR, LF */
  71         if (buf[n - 1] == '\n') n--;
  72         if (buf[n - 1] == '\r') n--;
  73         if (buf[n - 1] == '.') n--;
  74         buf[n] = '\0';
  75       }
  76       return (int)n;
  77     }
  78 
  79     if (errno != 0)
  80     {
  81       /* C runtime error that has no corresponding DOS error code */
  82       const char *s = strerror(errno);
  83       size_t n = strlen(s);
  84       if (n >= len) n = len - 1;
  85       strncpy(buf, s, n);
  86       buf[n] = '\0';
  87       return (int)n;
  88     }
  89     return 0;
  90 }
  91 #endif /* _WINDOWS */
  92 
  93 /*
  94  * Class:     sun_jvm_hotspot_asm_Disassembler
  95  * Method:    load_library
  96  * Signature: (Ljava/lang/String;)L
  97  */
  98 JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_asm_Disassembler_load_1library(JNIEnv * env,
  99                                                                            jclass disclass,
 100                                                                            jstring libname_s) {
 101   uintptr_t func = 0;
 102   const char* error_message = NULL;
 103   jboolean isCopy;
 104   const char * libname = (*env)->GetStringUTFChars(env, libname_s, &isCopy);
 105   char buffer[128];
 106 #ifdef _WINDOWS
 107   HINSTANCE hsdis_handle;
 108   snprintf(buffer, sizeof(buffer), "%s.dll", libname);
 109   hsdis_handle = LoadLibrary(buffer);
 110   if (hsdis_handle != NULL) {
 111     func = (uintptr_t)GetProcAddress(hsdis_handle, "decode_instructions_virtual");
 112   }
 113   if (func == 0) {
 114     getLastErrorString(buffer, sizeof(buffer));
 115     error_message = buffer;
 116   }
 117 #else
 118   void* hsdis_handle;
 119   snprintf(buffer, sizeof(buffer), "%s.so", libname);
 120   hsdis_handle = dlopen(buffer, RTLD_LAZY | RTLD_GLOBAL);
 121   if (hsdis_handle != NULL) {
 122     func = (uintptr_t)dlsym(hsdis_handle, "decode_instructions_virtual");
 123   }
 124   if (func == 0) {
 125     error_message = dlerror();
 126   }
 127 #endif
 128   (*env)->ReleaseStringUTFChars(env, libname_s, libname);
 129   if (func == 0) {
 130     jclass eclass = (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException");
 131     (*env)->ThrowNew(env, eclass, error_message);
 132   }
 133   return (jlong)func;
 134 }
 135 
 136 typedef void* (*decode_func)(uintptr_t start_va, uintptr_t end_va,
 137                              unsigned char* start, uintptr_t length,
 138                              void* (*event_callback)(void*, const char*, void*),
 139                              void* event_stream,
 140                              int (*printf_callback)(void*, const char*, ...),
 141                              void* printf_stream,
 142                              const char* options);
 143 
 144 typedef struct {
 145   JNIEnv* env;
 146   jobject dis;
 147   jobject visitor;
 148   jmethodID handle_event;
 149   jmethodID raw_print;
 150   char buffer[4096];
 151 } decode_env;
 152 
 153 static void* event_to_env(void* env_pv, const char* event, void* arg) {
 154   decode_env* denv = (decode_env*)env_pv;
 155   JNIEnv* env = denv->env;
 156   jstring event_string = (*env)->NewStringUTF(env, event);
 157   jlong result = (*env)->CallLongMethod(env, denv->dis, denv->handle_event, denv->visitor,
 158                                         event_string, (jlong) (uintptr_t)arg);
 159   if ((*env)->ExceptionOccurred(env) != NULL) {
 160     /* ignore exceptions for now */
 161     (*env)->ExceptionClear(env);
 162     result = 0;
 163   }
 164   return (void*)(uintptr_t)result;
 165 }
 166 
 167 static int printf_to_env(void* env_pv, const char* format, ...) {
 168   jstring output;
 169   va_list ap;
 170   int cnt;
 171   decode_env* denv = (decode_env*)env_pv;
 172   JNIEnv* env = denv->env;
 173   size_t flen = strlen(format);
 174   const char* raw = NULL;
 175 
 176   if (flen == 0)  return 0;
 177   if (flen < 2 ||
 178       strchr(format, '%') == NULL) {
 179     raw = format;
 180   } else if (format[0] == '%' && format[1] == '%' &&
 181              strchr(format+2, '%') == NULL) {
 182     // happens a lot on machines with names like %foo
 183     flen--;
 184     raw = format+1;
 185   }
 186   if (raw != NULL) {
 187     jstring output = (*env)->NewStringUTF(env, raw);
 188     (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output);
 189     if ((*env)->ExceptionOccurred(env) != NULL) {
 190       /* ignore exceptions for now */
 191       (*env)->ExceptionClear(env);
 192     }
 193     return (int) flen;
 194   }
 195   va_start(ap, format);
 196   cnt = vsnprintf(denv->buffer, sizeof(denv->buffer), format, ap);
 197   va_end(ap);
 198   
 199   output = (*env)->NewStringUTF(env, denv->buffer);
 200   (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output);
 201   if ((*env)->ExceptionOccurred(env) != NULL) {
 202     /* ignore exceptions for now */
 203     (*env)->ExceptionClear(env);
 204   }
 205   return cnt;
 206 }
 207 
 208 /*
 209  * Class:     sun_jvm_hotspot_asm_Disassembler
 210  * Method:    decode
 211  * Signature: (Lsun/jvm/hotspot/asm/InstructionVisitor;J[BLjava/lang/String;J)V
 212  */
 213 JNIEXPORT void JNICALL Java_sun_jvm_hotspot_asm_Disassembler_decode(JNIEnv * env,
 214                                                                     jobject dis, 
 215                                                                     jobject visitor,
 216                                                                     jlong startPc,
 217                                                                     jbyteArray code,
 218                                                                     jstring options_s,
 219                                                                     jlong decode_instructions_virtual) {
 220   jboolean isCopy;
 221   jbyte* start = (*env)->GetByteArrayElements(env, code, &isCopy);
 222   jbyte* end = start + (*env)->GetArrayLength(env, code);
 223   const char * options = (*env)->GetStringUTFChars(env, options_s, &isCopy);
 224   jclass disclass = (*env)->GetObjectClass(env, dis);
 225 
 226   decode_env denv;
 227   denv.env = env;
 228   denv.dis = dis;
 229   denv.visitor = visitor;
 230 
 231   denv.handle_event = (*env)->GetMethodID(env, disclass, "handleEvent",
 232                                           "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;J)J");
 233   if ((*env)->ExceptionOccurred(env)) {
 234     return;
 235   }
 236   
 237   denv.raw_print = (*env)->GetMethodID(env, disclass, "rawPrint",
 238                                        "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;)V");
 239   if ((*env)->ExceptionOccurred(env)) {
 240     return;
 241   }
 242   
 243   (*(decode_func)(uintptr_t)decode_instructions_virtual)(startPc,
 244                                                          startPc + end - start,
 245                                                          (unsigned char*)start,
 246                                                          end - start,
 247                                                          &event_to_env,  (void*) &denv,
 248                                                          &printf_to_env, (void*) &denv,
 249                                                          options);
 250 
 251   /* cleanup */
 252   (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT);
 253   (*env)->ReleaseStringUTFChars(env, options_s, options);
 254 }