stacktrace.cc 17.3 KB
Newer Older
1 2
// stacktrace.cc - Functions for unwinding & inspecting the call stack.

3
/* Copyright (C) 2005, 2006  Free Software Foundation
4 5 6 7 8 9 10 11

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

#include <config.h>
12
#include <platform.h>
13 14 15 16 17 18 19 20

#include <jvm.h>
#include <gcj/cni.h>
#include <java-interp.h>
#include <java-stack.h>

#include <stdio.h>

21
#include <java/lang/Boolean.h>
22 23
#include <java/lang/Class.h>
#include <java/lang/Long.h>
24
#include <java/security/AccessController.h>
25 26
#include <java/util/ArrayList.h>
#include <java/util/IdentityHashMap.h>
27
#include <gnu/classpath/jdwp/Jdwp.h>
28 29
#include <gnu/java/lang/MainThread.h>
#include <gnu/gcj/runtime/NameFinder.h>
30
#include <gnu/gcj/runtime/StringBuffer.h>
31 32

#include <sysdep/backtrace.h>
33
#include <sysdep/descriptor.h>
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

using namespace java::lang;
using namespace java::lang::reflect;
using namespace java::util;
using namespace gnu::gcj::runtime;

// Maps ncode values to their containing native class.
// NOTE: Currently this Map contradicts class GC for native classes. This map
// (and the "new class stack") will need to use WeakReferences in order to 
// enable native class GC.
static java::util::IdentityHashMap *ncodeMap;

// Check the "class stack" for any classes initialized since we were last 
// called, and add them to ncodeMap.
void 
_Jv_StackTrace::UpdateNCodeMap ()
{
  // The Map should be large enough so that a typical Java app doesn't cause 
  // it to rehash, without using too much memory. ~5000 entries should be 
  // enough.
  if (ncodeMap == NULL)
    ncodeMap = new java::util::IdentityHashMap (5087);
  
  jclass klass;
  while ((klass = _Jv_PopClass ()))
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    if (!_Jv_IsInterpretedClass (klass))
      {
	//printf ("got %s\n", klass->name->data);
	for (int i = 0; i < klass->method_count; i++)
	  {
	    _Jv_Method *method = &klass->methods[i];
	    void *ncode = method->ncode;
	    // Add non-abstract methods to ncodeMap.
	    if (ncode)
	      {
		ncode = UNWRAP_FUNCTION_DESCRIPTOR (ncode);
		ncodeMap->put ((java::lang::Object *) ncode, klass);
	      }
	  }
      }
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
}

// Given a native frame, return the class which this code belongs 
// to. Returns NULL if this IP is not associated with a native Java class.
// If NCODE is supplied, it will be set with the ip for the entry point of the 
// enclosing method.
jclass
_Jv_StackTrace::ClassForFrame (_Jv_StackFrame *frame)
{
  JvAssert (frame->type == frame_native);
  jclass klass = NULL;

  // look it up in ncodeMap
  if (frame->start_ip)
    klass = (jclass) ncodeMap->get ((jobject) frame->start_ip);

  return klass;
}

_Unwind_Reason_Code
_Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr)
{
  _Jv_UnwindState *state = (_Jv_UnwindState *) state_ptr;
  jint pos = state->pos;

  // Check if the trace buffer needs to be extended.
  if (pos == state->length)
    {
102
      int newLength = state->length * 2;
103 104 105 106 107
      void *newFrames = _Jv_AllocBytes (newLength * sizeof(_Jv_StackFrame));
      memcpy (newFrames, state->frames, state->length * sizeof(_Jv_StackFrame));      
      state->frames = (_Jv_StackFrame *) newFrames;
      state->length = newLength;
    }
108 109 110

  void *func_addr = (void *) _Unwind_GetRegionStart (context);

111 112 113 114 115
  // If we see the interpreter's main function, "pop" an entry off the 
  // interpreter stack and use that instead, so that the trace goes through 
  // the java code and not the interpreter itself. This assumes a 1:1 
  // correspondance between call frames in the interpreted stack and occurances
  // of _Jv_InterpMethod::run() on the native stack.
116
#ifdef INTERPRETER
117 118 119 120 121 122 123
  void *interp_run = NULL;
  
  if (::gnu::classpath::jdwp::Jdwp::isDebugging)
  	interp_run = (void *) &_Jv_InterpMethod::run_debug;
  else
    interp_run = (void *) &_Jv_InterpMethod::run;
  	
124
  if (func_addr == UNWRAP_FUNCTION_DESCRIPTOR (interp_run))
125 126 127 128 129 130 131
    {
      state->frames[pos].type = frame_interpreter;
      state->frames[pos].interp.meth = state->interp_frame->self;
      state->frames[pos].interp.pc = state->interp_frame->pc;
      state->interp_frame = state->interp_frame->next;
    }
  else
132
#endif
133
    {
134 135 136 137 138 139 140 141 142
      _Unwind_Ptr ip;
      int ip_before_insn = 0;
      ip = _Unwind_GetIPInfo (context, &ip_before_insn);

      // If the unwinder gave us a 'return' address, roll it back a little
      // to ensure we get the correct line number for the call itself.
      if (! ip_before_insn)
	--ip;

143
      state->frames[pos].type = frame_native;
144
      state->frames[pos].ip = (void *) ip;
145
      state->frames[pos].start_ip = func_addr;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    }

  _Unwind_Reason_Code result = _URC_NO_REASON;
  if (state->trace_function != NULL)
    result = (state->trace_function) (state);
  state->pos++;
  return result;
}

// Return a raw stack trace from the current point of execution. The raw 
// trace will include all functions that have unwind info.
_Jv_StackTrace *
_Jv_StackTrace::GetStackTrace(void)
{
  int trace_size = 100;
  _Jv_StackFrame frames[trace_size];
  _Jv_UnwindState state (trace_size);
  state.frames = (_Jv_StackFrame *) &frames;

  _Unwind_Backtrace (UnwindTraceFn, &state);
  
  // Copy the trace and return it.
  int traceSize = sizeof (_Jv_StackTrace) + 
    (sizeof (_Jv_StackFrame) * state.pos);
  _Jv_StackTrace *trace = (_Jv_StackTrace *) _Jv_AllocBytes (traceSize);
  trace->length = state.pos;
  memcpy (trace->frames, state.frames, sizeof (_Jv_StackFrame) * state.pos);  
  return trace;
}

void
_Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder, 
178 179
				      jstring *sourceFileName, jint *lineNum,
				      jstring *methodName)
180
{
181
#ifdef INTERPRETER
182 183 184 185 186 187
  if (frame->type == frame_interpreter)
    {
      _Jv_InterpMethod *interp_meth = frame->interp.meth;
      _Jv_InterpClass *interp_class = 
	 (_Jv_InterpClass *) interp_meth->defining_class->aux_info;
      *sourceFileName = interp_class->source_file_name;
188 189 190
      // The interpreter advances the PC before executing an instruction,
      // so roll-back 1 byte to ensure the line number is accurate.
      *lineNum = interp_meth->get_source_line(frame->interp.pc - 1);
191 192
      return;
    }
193
#endif
194 195 196 197

  // Use _Jv_platform_dladdr() to determine in which binary the address IP
  // resides.
  _Jv_AddrInfo info;
198
  jstring binaryName = NULL;
199
  const char *argv0 = _Jv_GetSafeArg(0);
200 201 202

  void *ip = frame->ip;
  _Unwind_Ptr offset = 0;
203

204
  if (_Jv_platform_dladdr (ip, &info))
205
    {
206 207
      if (info.file_name)
	binaryName = JvNewStringUTF (info.file_name);
208 209 210
      else
        return;

211 212
      if (*methodName == NULL && info.sym_name)
	*methodName = JvNewStringUTF (info.sym_name);
213

214
      // addr2line expects relative addresses for shared libraries.
215
      if (strcmp (info.file_name, argv0) == 0)
216 217
        offset = (_Unwind_Ptr) ip;
      else
218
        offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.base;
219 220 221 222

      finder->lookup (binaryName, (jlong) offset);
      *sourceFileName = finder->getSourceFile();
      *lineNum = finder->getLineNum();
223 224 225 226 227 228 229 230 231 232 233
      if (*lineNum == -1 && NameFinder::showRaw())
        {
          gnu::gcj::runtime::StringBuffer *t =
            new gnu::gcj::runtime::StringBuffer(binaryName);
          t->append ((jchar)' ');
          t->append ((jchar)'[');
          // + 1 to compensate for the - 1 adjustment above;
          t->append (Long::toHexString (offset + 1));
          t->append ((jchar)']');
          *sourceFileName = t->toString();
        }
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    }
}

// Look up class and method info for the given stack frame, setting 
// frame->klass and frame->meth if they are known.
void
_Jv_StackTrace::FillInFrameInfo (_Jv_StackFrame *frame)
{
  jclass klass = NULL;
  _Jv_Method *meth = NULL;
  
  if (frame->type == frame_native)
    {
      klass = _Jv_StackTrace::ClassForFrame (frame);

      if (klass != NULL)
	// Find method in class
	for (int j = 0; j < klass->method_count; j++)
	  {
253
	    void *wncode = UNWRAP_FUNCTION_DESCRIPTOR (klass->methods[j].ncode);
254
	    if (wncode == frame->start_ip)
255 256 257 258 259 260
	      {
		meth = &klass->methods[j];
		break;
	      }
	  }
    }
261
#ifdef INTERPRETER
262 263 264 265 266 267
  else if (frame->type == frame_interpreter)
    {
      _Jv_InterpMethod *interp_meth = frame->interp.meth;
      klass = interp_meth->defining_class;
      meth = interp_meth->self;
    }
268
#endif
269 270 271 272 273 274 275 276 277 278 279 280 281 282
  else
    JvFail ("Unknown frame type");
  
  frame->klass = klass;
  frame->meth = meth;
}

// Convert raw stack frames to a Java array of StackTraceElement objects.
JArray< ::java::lang::StackTraceElement *>*
_Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, 
  Throwable *throwable __attribute__((unused)))
{
  ArrayList *list = new ArrayList ();

283
#if defined (SJLJ_EXCEPTIONS) && ! defined (WIN32)
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
  // We can't use the nCodeMap without unwinder support. Instead,
  // fake the method name by giving the IP in hex - better than nothing.  
  jstring hex = JvNewStringUTF ("0x");

  for (int i = 0; i < trace->length; i++)
    {
      jstring sourceFileName = NULL;
      jint lineNum = -1;
      _Jv_StackFrame *frame = &trace->frames[i];
      
      jstring className = NULL;
      jstring methodName = hex->concat (Long::toHexString ((jlong) frame->ip));

      StackTraceElement *element = new StackTraceElement (sourceFileName, 
	lineNum, className, methodName, 0);    
      list->add (element);
    }

302
#else /* SJLJ_EXCEPTIONS && !WIN32 */
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

  //JvSynchronized (ncodeMap);
  UpdateNCodeMap ();

  NameFinder *finder = new NameFinder();
  int start_idx = 0;
  int end_idx = trace->length - 1;

  // First pass: strip superfluous frames from beginning and end of the trace.  
  for (int i = 0; i < trace->length; i++)
    {
      _Jv_StackFrame *frame = &trace->frames[i];
      FillInFrameInfo (frame);

      if (!frame->klass || !frame->meth)
        // Not a Java frame.
        continue;

      // Throw away the top of the stack till we see:
      //  - the constructor(s) of this Throwable, or
      //  - the Throwable.fillInStackTrace call.
      if (frame->klass == throwable->getClass()
          && strcmp (frame->meth->name->chars(), "<init>") == 0)
        start_idx = i + 1;

      if (frame->klass == &Throwable::class$
          && strcmp (frame->meth->name->chars(), "fillInStackTrace") == 0)
	start_idx = i + 1;

      // End the trace at the application's main() method if we see call_main.
      if (frame->klass == &gnu::java::lang::MainThread::class$
          && strcmp (frame->meth->name->chars(), "call_main") == 0)
	end_idx = i - 1;
    }
  
338 339 340
  const jboolean remove_unknown 
    = gnu::gcj::runtime::NameFinder::removeUnknown();

341 342 343 344 345
  // Second pass: Look up line-number info for remaining frames.
  for (int i = start_idx; i <= end_idx; i++)
    {
      _Jv_StackFrame *frame = &trace->frames[i];
      
346 347
      if (frame->klass == NULL && remove_unknown)
	// Not a Java frame.
348
	continue;
349 350 351 352 353

      jstring className = NULL;
      if (frame->klass != NULL)
	className = frame->klass->getName ();

354 355 356 357 358 359 360
      jstring methodName = NULL;
      if (frame->meth)
        methodName = JvNewStringUTF (frame->meth->name->chars());
      
      jstring sourceFileName = NULL;
      jint lineNum = -1;
      
361 362
      getLineNumberForFrame(frame, finder, &sourceFileName, &lineNum, 
			    &methodName);
363 364 365 366 367 368 369
      
      StackTraceElement *element = new StackTraceElement (sourceFileName, lineNum,
        className, methodName, 0);
      list->add (element);
    }
  
  finder->close();
370
#endif /* SJLJ_EXCEPTIONS && !WIN32 */
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

  JArray<Object *> *array = JvNewObjectArray (list->size (), 
    &StackTraceElement::class$, NULL);

  return (JArray<StackTraceElement *>*) list->toArray (array);
}

struct CallingClassTraceData
{
  jclass checkClass;    
  jclass foundClass;
  _Jv_Method *foundMeth;
  bool seen_checkClass;
};

_Unwind_Reason_Code
_Jv_StackTrace::calling_class_trace_fn (_Jv_UnwindState *state)
{
  CallingClassTraceData *trace_data = (CallingClassTraceData *)
    state->trace_data;
  _Jv_StackFrame *frame = &state->frames[state->pos];
  FillInFrameInfo (frame);

  if (trace_data->seen_checkClass
      && frame->klass
      && frame->klass != trace_data->checkClass)
    {
      trace_data->foundClass = frame->klass;
      trace_data->foundMeth = frame->meth;
      return _URC_NORMAL_STOP;
    }
  
  if (frame->klass == trace_data->checkClass)
    trace_data->seen_checkClass = true;
    
  return _URC_NO_REASON;
}

// Find the class immediately above the given class on the call stack. Any 
// intermediate non-Java 
// frames are ignored. If the calling class could not be determined (eg because 
// the unwinder is not supported on this platform), NULL is returned.
// This function is used to implement calling-classloader checks and reflection
// accessibility checks.
// CHECKCLASS is typically the class calling GetCallingClass. The first class
// above CHECKCLASS on the call stack will be returned.
jclass
_Jv_StackTrace::GetCallingClass (jclass checkClass)
{
  jclass result = NULL;
  GetCallerInfo (checkClass, &result, NULL);
  return result;
}

void
_Jv_StackTrace::GetCallerInfo (jclass checkClass, jclass *caller_class,
  _Jv_Method **caller_meth)
{
  int trace_size = 20;
  _Jv_StackFrame frames[trace_size];
  _Jv_UnwindState state (trace_size);
  state.frames = (_Jv_StackFrame *) &frames;

  CallingClassTraceData trace_data;
  trace_data.checkClass = checkClass;
  trace_data.seen_checkClass = false;
  trace_data.foundClass = NULL;
  trace_data.foundMeth = NULL;

  state.trace_function = calling_class_trace_fn;
  state.trace_data = (void *) &trace_data;

  //JvSynchronized (ncodeMap);
  UpdateNCodeMap ();

  _Unwind_Backtrace (UnwindTraceFn, &state);
  
  if (caller_class)
    *caller_class = trace_data.foundClass;
  if (caller_meth)
    *caller_meth = trace_data.foundMeth;
}

// Return a java array containing the Java classes on the stack above CHECKCLASS.
JArray<jclass> *
_Jv_StackTrace::GetClassContext (jclass checkClass)
{
  JArray<jclass> *result = NULL;

  int trace_size = 100;
  _Jv_StackFrame frames[trace_size];
  _Jv_UnwindState state (trace_size);
  state.frames = (_Jv_StackFrame *) &frames;

  //JvSynchronized (ncodeMap);
  UpdateNCodeMap ();

  _Unwind_Backtrace (UnwindTraceFn, &state);

  // Count the number of Java frames on the stack.
  int jframe_count = 0;
  bool seen_checkClass = false;
  int start_pos = -1;
  for (int i = 0; i < state.pos; i++)
    {
      _Jv_StackFrame *frame = &state.frames[i];
      FillInFrameInfo (frame);
      
479
      if (seen_checkClass)
480
	{
481 482 483 484 485 486
	  if (frame->klass)
	    {
	      jframe_count++;
	      if (start_pos == -1)
		start_pos = i;
	    }
487
	}
488 489
      else
	seen_checkClass = frame->klass == checkClass;
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
    }
  result = (JArray<jclass> *) _Jv_NewObjectArray (jframe_count, &Class::class$, NULL);
  int pos = 0;
  
  for (int i = start_pos; i < state.pos; i++)
    {
      _Jv_StackFrame *frame = &state.frames[i];
      if (frame->klass)
        elements(result)[pos++] = frame->klass;
    }
  return result;
}

_Unwind_Reason_Code
_Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state)
{
  _Jv_StackFrame *frame = &state->frames[state->pos];
  FillInFrameInfo (frame);
  
  ClassLoader *classLoader = NULL;

  if (frame->klass)
    {
      classLoader = frame->klass->getClassLoaderInternal();
514
#ifdef INTERPRETER
Tom Tromey committed
515
      if (classLoader != NULL)
516 517 518 519
        {
          state->trace_data = (void *) classLoader;
	  return _URC_NORMAL_STOP;
	}
520
#endif
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
    }

  return _URC_NO_REASON;
}

ClassLoader *
_Jv_StackTrace::GetFirstNonSystemClassLoader ()
{
  int trace_size = 32;
  _Jv_StackFrame frames[trace_size];
  _Jv_UnwindState state (trace_size);
  state.frames = (_Jv_StackFrame *) &frames;
  state.trace_function = non_system_trace_fn;
  state.trace_data = NULL;

  //JvSynchronized (ncodeMap);
  UpdateNCodeMap ();
  
  _Unwind_Backtrace (UnwindTraceFn, &state);

  if (state.trace_data)
    return (ClassLoader *) state.trace_data;
  
  return NULL;
}
546

547 548 549 550 551 552
struct AccessControlTraceData
{
  jint length;
  jboolean privileged;
};

553 554 555
_Unwind_Reason_Code
_Jv_StackTrace::accesscontrol_trace_fn (_Jv_UnwindState *state)
{
556 557
  AccessControlTraceData *trace_data = (AccessControlTraceData *)
    state->trace_data;
558 559 560
  _Jv_StackFrame *frame = &state->frames[state->pos];
  FillInFrameInfo (frame);

561 562 563
  if (!(frame->klass && frame->meth))
    return _URC_NO_REASON;

564 565 566 567 568
  trace_data->length++;

  // If the previous frame was a call to doPrivileged, then this is
  // the last frame we look at.
  if (trace_data->privileged)
569 570 571 572
    return _URC_NORMAL_STOP;
  
  if (frame->klass == &::java::security::AccessController::class$
      && strcmp (frame->meth->name->chars(), "doPrivileged") == 0)
573
    trace_data->privileged = true;
574 575 576 577

  return _URC_NO_REASON;
}

578
jobjectArray
579
_Jv_StackTrace::GetAccessControlStack (void)
580
{
581 582 583 584
  int trace_size = 100;
  _Jv_StackFrame frames[trace_size];
  _Jv_UnwindState state (trace_size);
  state.frames = (_Jv_StackFrame *) &frames;
585 586 587 588 589

  AccessControlTraceData trace_data;
  trace_data.length = 0;
  trace_data.privileged = false;
  
590
  state.trace_function = accesscontrol_trace_fn;
591
  state.trace_data = (void *) &trace_data;
592 593

  UpdateNCodeMap();
594 595
  _Unwind_Backtrace (UnwindTraceFn, &state);

596
  JArray<jclass> *classes = (JArray<jclass> *)
597 598
    _Jv_NewObjectArray (trace_data.length, &::java::lang::Class::class$, NULL);
  jclass *c = elements (classes);
599

600
  for (int i = 0, j = 0; i < state.pos; i++)
601
    {
602
      _Jv_StackFrame *frame = &state.frames[i];
603 604 605 606 607 608
      if (!frame->klass || !frame->meth)
	continue;
      c[j] = frame->klass;
      j++;
    }

609 610 611 612 613 614 615
  jobjectArray result =
    (jobjectArray) _Jv_NewObjectArray (2, &::java::lang::Object::class$,
					 NULL);
  jobject *r = elements (result);
  r[0] = (jobject) classes;
  r[1] = (jobject) new Boolean (trace_data.privileged);
  
616 617
  return result;
}