X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fstacktrace.cc;h=d8d1f38ae6c8b2be22a2258727f51dfb202881a4;hb=696a0d4efbaf40b5ab4858eadf37c4a61bee70b3;hp=0777d903a2173fa48dedd18252f6ee0761f0aa99;hpb=04c95bc933562928623c859240c17a70f2a1311c;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/stacktrace.cc b/libjava/stacktrace.cc index 0777d903a21..d8d1f38ae6c 100644 --- a/libjava/stacktrace.cc +++ b/libjava/stacktrace.cc @@ -1,6 +1,6 @@ // stacktrace.cc - Functions for unwinding & inspecting the call stack. -/* Copyright (C) 2005 Free Software Foundation +/* Copyright (C) 2005, 2006 Free Software Foundation This file is part of libgcj. @@ -9,42 +9,45 @@ Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ #include +#include #include #include #include #include -#ifdef HAVE_DLFCN_H -#include -#endif - #include +#include #include #include +#include +#include #include #include +#include +#include #include #include +#include #include +#include using namespace java::lang; using namespace java::lang::reflect; using namespace java::util; using namespace gnu::gcj::runtime; -struct _Jv_FindCallingClassState: _Jv_UnwindState -{ - jclass result; -}; +#ifdef __ARM_EABI_UNWINDER__ +#define _URC_NORMAL_STOP _URC_FAILURE +#endif // 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; +java::util::IdentityHashMap *_Jv_StackTrace::ncodeMap; // Check the "class stack" for any classes initialized since we were last // called, and add them to ncodeMap. @@ -61,18 +64,15 @@ _Jv_StackTrace::UpdateNCodeMap () while ((klass = _Jv_PopClass ())) { //printf ("got %s\n", klass->name->data); -#ifdef INTERPRETER - JvAssert (! _Jv_IsInterpretedClass (klass)); -#endif - for (int i=0; i < klass->method_count; i++) - { + 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 (method->ncode) + if (ncode) { - //printf("map->put 0x%x / %s.%s\n", method->ncode, klass->name->data, - // method->name->data); - ncodeMap->put ((java::lang::Object *) method->ncode, klass); + ncode = UNWRAP_FUNCTION_DESCRIPTOR (ncode); + ncodeMap->put ((java::lang::Object *) ncode, klass); } } } @@ -87,12 +87,16 @@ _Jv_StackTrace::ClassForFrame (_Jv_StackFrame *frame) { JvAssert (frame->type == frame_native); jclass klass = NULL; - // use _Unwind_FindEnclosingFunction to find start of method - //void *entryPoint = _Unwind_FindEnclosingFunction (ip); // look it up in ncodeMap if (frame->start_ip) - klass = (jclass) ncodeMap->get ((jobject) frame->start_ip); + { + klass = (jclass) ncodeMap->get ((jobject) frame->start_ip); + + // Exclude interpreted classes + if (klass != NULL && _Jv_IsInterpretedClass (klass)) + klass = NULL; + } return klass; } @@ -106,36 +110,68 @@ _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr) // Check if the trace buffer needs to be extended. if (pos == state->length) { - int newLength = state->length *= 2; + int newLength = state->length * 2; 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; } - - _Unwind_Ptr func_addr = _Unwind_GetRegionStart (context); - + + void *func_addr = (void *) _Unwind_GetRegionStart (context); + // 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. - if (func_addr == (_Unwind_Ptr) &_Jv_InterpMethod::run) +#ifdef INTERPRETER + void *interp_run = NULL; + + if (::gnu::classpath::jdwp::Jdwp::isDebugging) + interp_run = (void *) &_Jv_InterpMethod::run_debug; + else + interp_run = (void *) &_Jv_InterpMethod::run; + + if (func_addr == UNWRAP_FUNCTION_DESCRIPTOR (interp_run)) { state->frames[pos].type = frame_interpreter; - state->frames[pos].interp.meth = state->interp_frame->self; + _Jv_Frame *frame = static_cast<_Jv_Frame *> (state->interp_frame); + state->frames[pos].interp.meth + = static_cast<_Jv_InterpMethod *> (frame->self); state->frames[pos].interp.pc = state->interp_frame->pc; - state->interp_frame = state->interp_frame->next; + state->interp_frame = state->interp_frame->next_interp; } - else + else + // We handle proxies in the same way as interpreted classes + if (_Jv_is_proxy (func_addr)) { + state->frames[pos].type = frame_proxy; + state->frames[pos].proxyClass = state->interp_frame->proxyClass; + state->frames[pos].proxyMethod = state->interp_frame->proxyMethod; + state->interp_frame = state->interp_frame->next_interp; + } + else +#endif + { +#ifdef HAVE_GETIPINFO + _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; +#endif state->frames[pos].type = frame_native; +#ifdef HAVE_GETIPINFO + state->frames[pos].ip = (void *) ip; +#else state->frames[pos].ip = (void *) _Unwind_GetIP (context); - state->frames[pos].start_ip = (void *) func_addr; +#endif + state->frames[pos].start_ip = func_addr; } - //printf ("unwind ip: %p\n", _Unwind_GetIP (context)); - _Unwind_Reason_Code result = _URC_NO_REASON; if (state->trace_function != NULL) result = (state->trace_function) (state); @@ -153,13 +189,7 @@ _Jv_StackTrace::GetStackTrace(void) _Jv_UnwindState state (trace_size); state.frames = (_Jv_StackFrame *) &frames; -#ifdef SJLJ_EXCEPTIONS - // The Unwind interface doesn't work with the SJLJ exception model. - // Fall back to a platform-specific unwinder. - fallback_backtrace (&state); -#else /* SJLJ_EXCEPTIONS */ _Unwind_Backtrace (UnwindTraceFn, &state); -#endif /* SJLJ_EXCEPTIONS */ // Copy the trace and return it. int traceSize = sizeof (_Jv_StackTrace) + @@ -172,54 +202,76 @@ _Jv_StackTrace::GetStackTrace(void) void _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder, - jstring *sourceFileName, jint *lineNum) + jstring *sourceFileName, jint *lineNum, + jstring *methodName) { +#ifdef INTERPRETER 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; - *lineNum = interp_meth->get_source_line(frame->interp.pc); + // 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); + return; + } +#endif + + if (frame->type == frame_proxy) + { + *sourceFileName = NULL; + *lineNum = 0; return; } - // Use dladdr() to determine in which binary the address IP resides. -#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR) - extern char **_Jv_argv; - Dl_info info; + + // Use _Jv_platform_dladdr() to determine in which binary the address IP + // resides. + _Jv_AddrInfo info; jstring binaryName = NULL; + const char *argv0 = _Jv_GetSafeArg(0); void *ip = frame->ip; _Unwind_Ptr offset = 0; - - if (dladdr (ip, &info)) + + if (_Jv_platform_dladdr (ip, &info)) { - if (info.dli_fname) - binaryName = JvNewStringUTF (info.dli_fname); + if (info.file_name) + binaryName = JvNewStringUTF (info.file_name); else return; + if (*methodName == NULL && info.sym_name) + *methodName = JvNewStringUTF (info.sym_name); + // addr2line expects relative addresses for shared libraries. - if (strcmp (info.dli_fname, _Jv_argv[0]) == 0) + if (strcmp (info.file_name, argv0) == 0) offset = (_Unwind_Ptr) ip; else - offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.dli_fbase; + offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.base; - //printf ("linenum ip: %p\n", ip); - //printf ("%s: 0x%x\n", info.dli_fname, offset); - //offset -= sizeof(void *); - +#ifndef HAVE_GETIPINFO // The unwinder gives us the return address. In order to get the right // line number for the stack trace, roll it back a little. offset -= 1; +#endif - // printf ("%s: 0x%x\n", info.dli_fname, offset); - finder->lookup (binaryName, (jlong) offset); *sourceFileName = finder->getSourceFile(); *lineNum = finder->getLineNum(); + 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(); + } } -#endif } // Look up class and method info for the given stack frame, setting @@ -238,19 +290,27 @@ _Jv_StackTrace::FillInFrameInfo (_Jv_StackFrame *frame) // Find method in class for (int j = 0; j < klass->method_count; j++) { - if (klass->methods[j].ncode == frame->start_ip) + void *wncode = UNWRAP_FUNCTION_DESCRIPTOR (klass->methods[j].ncode); + if (wncode == frame->start_ip) { meth = &klass->methods[j]; break; } } } + else if (frame->type == frame_proxy) + { + klass = frame->proxyClass; + meth = frame->proxyMethod; + } +#ifdef INTERPRETER else if (frame->type == frame_interpreter) { _Jv_InterpMethod *interp_meth = frame->interp.meth; klass = interp_meth->defining_class; meth = interp_meth->self; } +#endif else JvFail ("Unknown frame type"); @@ -265,7 +325,7 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, { ArrayList *list = new ArrayList (); -#ifdef SJLJ_EXCEPTIONS +#if defined (SJLJ_EXCEPTIONS) && ! defined (WIN32) // 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"); @@ -284,7 +344,7 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, list->add (element); } -#else /* SJLJ_EXCEPTIONS */ +#else /* SJLJ_EXCEPTIONS && !WIN32 */ //JvSynchronized (ncodeMap); UpdateNCodeMap (); @@ -320,16 +380,22 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, end_idx = i - 1; } + const jboolean remove_unknown + = gnu::gcj::runtime::NameFinder::removeUnknown(); + // 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]; - if (frame->klass == NULL) - // Not a Java frame. + if (frame->klass == NULL && remove_unknown) + // Not a Java frame. continue; - - jstring className = frame->klass->getName (); + + jstring className = NULL; + if (frame->klass != NULL) + className = frame->klass->getName (); + jstring methodName = NULL; if (frame->meth) methodName = JvNewStringUTF (frame->meth->name->chars()); @@ -337,7 +403,8 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, jstring sourceFileName = NULL; jint lineNum = -1; - getLineNumberForFrame(frame, finder, &sourceFileName, &lineNum); + getLineNumberForFrame(frame, finder, &sourceFileName, &lineNum, + &methodName); StackTraceElement *element = new StackTraceElement (sourceFileName, lineNum, className, methodName, 0); @@ -345,7 +412,7 @@ _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace, } finder->close(); -#endif /* SJLJ_EXCEPTIONS */ +#endif /* SJLJ_EXCEPTIONS && !WIN32 */ JArray *array = JvNewObjectArray (list->size (), &StackTraceElement::class$, NULL); @@ -404,7 +471,6 @@ void _Jv_StackTrace::GetCallerInfo (jclass checkClass, jclass *caller_class, _Jv_Method **caller_meth) { -#ifndef SJLJ_EXCEPTIONS int trace_size = 20; _Jv_StackFrame frames[trace_size]; _Jv_UnwindState state (trace_size); @@ -428,76 +494,311 @@ _Jv_StackTrace::GetCallerInfo (jclass checkClass, jclass *caller_class, *caller_class = trace_data.foundClass; if (caller_meth) *caller_meth = trace_data.foundMeth; -#else - return NULL; -#endif } -// Return a java array containing the Java classes on the stack above CHECKCLASS. -JArray * -_Jv_StackTrace::GetClassContext (jclass checkClass) +_Unwind_Reason_Code +_Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state) { - JArray *result = NULL; + _Jv_StackFrame *frame = &state->frames[state->pos]; + FillInFrameInfo (frame); + + ClassLoader *classLoader = NULL; - int trace_size = 100; + if (frame->klass) + { + classLoader = frame->klass->getClassLoaderInternal(); +#ifdef INTERPRETER + if (classLoader != NULL) + { + state->trace_data = (void *) classLoader; + return _URC_NORMAL_STOP; + } +#endif + } + + 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; +} + +struct AccessControlTraceData +{ + jint length; + jboolean privileged; +}; + +_Unwind_Reason_Code +_Jv_StackTrace::accesscontrol_trace_fn (_Jv_UnwindState *state) +{ + AccessControlTraceData *trace_data = (AccessControlTraceData *) + state->trace_data; + _Jv_StackFrame *frame = &state->frames[state->pos]; + FillInFrameInfo (frame); + + if (!(frame->klass && frame->meth)) + return _URC_NO_REASON; + + 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) + return _URC_NORMAL_STOP; + + if (frame->klass == &::java::security::AccessController::class$ + && strcmp (frame->meth->name->chars(), "doPrivileged") == 0) + trace_data->privileged = true; + + return _URC_NO_REASON; +} + +jobjectArray +_Jv_StackTrace::GetAccessControlStack (void) +{ + int trace_size = 100; + _Jv_StackFrame frames[trace_size]; + _Jv_UnwindState state (trace_size); + state.frames = (_Jv_StackFrame *) &frames; + + AccessControlTraceData trace_data; + trace_data.length = 0; + trace_data.privileged = false; + + state.trace_function = accesscontrol_trace_fn; + state.trace_data = (void *) &trace_data; + + UpdateNCodeMap(); + _Unwind_Backtrace (UnwindTraceFn, &state); + + JArray *classes = (JArray *) + _Jv_NewObjectArray (trace_data.length, &::java::lang::Class::class$, NULL); + jclass *c = elements (classes); + + for (int i = 0, j = 0; i < state.pos; i++) + { + _Jv_StackFrame *frame = &state.frames[i]; + if (!frame->klass || !frame->meth) + continue; + c[j] = frame->klass; + j++; + } + + 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); + + return result; +} + +JArray * +_Jv_StackTrace::GetStackWalkerStack () +{ + int trace_size = 100; + _Jv_StackFrame frames[trace_size]; + _Jv_UnwindState state (trace_size); + state.frames = (_Jv_StackFrame *) &frames; + 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; + int num_frames = 0, start_frame = -1; + enum + { + VMSW_GETCLASSCONTEXT, + JLRM_INVOKE_OR_USER_FN, + USER_FN + } + expect = VMSW_GETCLASSCONTEXT; for (int i = 0; i < state.pos; i++) { _Jv_StackFrame *frame = &state.frames[i]; FillInFrameInfo (frame); - - if (seen_checkClass - && frame->klass - && frame->klass != checkClass) + if (!frame->klass || !frame->meth) + continue; + + switch (expect) { - jframe_count++; - if (start_pos == -1) - start_pos = i; + case VMSW_GETCLASSCONTEXT: + JvAssert ( + frame->klass == &::gnu::classpath::VMStackWalker::class$ + && strcmp (frame->meth->name->chars(), "getClassContext") == 0); + expect = JLRM_INVOKE_OR_USER_FN; + break; + + case JLRM_INVOKE_OR_USER_FN: + if (frame->klass != &::java::lang::reflect::Method::class$ + || strcmp (frame->meth->name->chars(), "invoke") != 0) + start_frame = i; + expect = USER_FN; + break; + + case USER_FN: + if (start_frame == -1) + start_frame = i; + break; } - if (!seen_checkClass - && frame->klass - && frame->klass == checkClass) - seen_checkClass = true; + if (start_frame != -1) + { + if (frame->klass == &::gnu::java::lang::MainThread::class$) + break; + num_frames++; + } } - result = (JArray *) _Jv_NewObjectArray (jframe_count, &Class::class$, NULL); - int pos = 0; - - for (int i = start_pos; i < state.pos; i++) + JvAssert (num_frames > 0 && start_frame > 0); + + JArray *result = (JArray *) + _Jv_NewObjectArray (num_frames, &::java::lang::Class::class$, NULL); + jclass *c = elements (result); + + for (int i = start_frame, j = 0; i < state.pos && j < num_frames; i++) { _Jv_StackFrame *frame = &state.frames[i]; - if (frame->klass) - elements(result)[pos++] = frame->klass; + if (!frame->klass || !frame->meth) + continue; + c[j] = frame->klass; + j++; } + return result; } +typedef enum + { + VMSW_GET_CALLING_ITEM, + JLRM_INVOKE_OR_CALLER, + CALLER, + CALLER_OF_CALLER + } gswcc_expect; + +struct StackWalkerTraceData +{ + gswcc_expect expect; + jclass result; +}; + _Unwind_Reason_Code -_Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state) +_Jv_StackTrace::stackwalker_trace_fn (_Jv_UnwindState *state) { + StackWalkerTraceData *trace_data = (StackWalkerTraceData *) + state->trace_data; _Jv_StackFrame *frame = &state->frames[state->pos]; FillInFrameInfo (frame); + + if (!(frame->klass && frame->meth)) + return _URC_NO_REASON; + + switch (trace_data->expect) + { + case VMSW_GET_CALLING_ITEM: + JvAssert (frame->klass == &::gnu::classpath::VMStackWalker::class$); + trace_data->expect = JLRM_INVOKE_OR_CALLER; + break; + + case JLRM_INVOKE_OR_CALLER: + if (frame->klass == &::java::lang::reflect::Method::class$ + && strcmp (frame->meth->name->chars(), "invoke") == 0) + trace_data->expect = CALLER; + else + trace_data->expect = CALLER_OF_CALLER; + break; + + case CALLER: + trace_data->expect = CALLER_OF_CALLER; + break; + + case CALLER_OF_CALLER: + trace_data->result = frame->klass; + return _URC_NORMAL_STOP; + } + + return _URC_NO_REASON; +} + +jclass +_Jv_StackTrace::GetStackWalkerCallingClass (void) +{ + int trace_size = 100; + _Jv_StackFrame frames[trace_size]; + _Jv_UnwindState state (trace_size); + state.frames = (_Jv_StackFrame *) &frames; + + StackWalkerTraceData trace_data; + trace_data.expect = VMSW_GET_CALLING_ITEM; + trace_data.result = NULL; - ClassLoader *classLoader = NULL; + state.trace_function = stackwalker_trace_fn; + state.trace_data = (void *) &trace_data; - if (frame->klass) + UpdateNCodeMap(); + _Unwind_Backtrace (UnwindTraceFn, &state); + + return trace_data.result; +} + +struct StackWalkerNNLTraceData +{ + gswcc_expect expect; + ClassLoader *result; +}; + +_Unwind_Reason_Code +_Jv_StackTrace::stackwalker_nnl_trace_fn (_Jv_UnwindState *state) +{ + StackWalkerNNLTraceData *trace_data = (StackWalkerNNLTraceData *) + state->trace_data; + _Jv_StackFrame *frame = &state->frames[state->pos]; + FillInFrameInfo (frame); + + if (!(frame->klass && frame->meth)) + return _URC_NO_REASON; + + switch (trace_data->expect) { - classLoader = frame->klass->getClassLoaderInternal(); - if (classLoader != NULL && classLoader != ClassLoader::systemClassLoader) - { - state->trace_data = (void *) classLoader; + case VMSW_GET_CALLING_ITEM: + JvAssert (frame->klass == &::gnu::classpath::VMStackWalker::class$); + trace_data->expect = JLRM_INVOKE_OR_CALLER; + break; + + case JLRM_INVOKE_OR_CALLER: + if (frame->klass == &::java::lang::reflect::Method::class$ + && strcmp (frame->meth->name->chars(), "invoke") == 0) + trace_data->expect = CALLER; + else + trace_data->expect = CALLER_OF_CALLER; + break; + + case CALLER: + trace_data->expect = CALLER_OF_CALLER; + break; + + case CALLER_OF_CALLER: + ClassLoader *cl = frame->klass->getClassLoaderInternal (); + if (cl != NULL) + { + trace_data->result = cl; return _URC_NORMAL_STOP; } } @@ -506,22 +807,22 @@ _Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state) } ClassLoader * -_Jv_StackTrace::GetFirstNonSystemClassLoader () +_Jv_StackTrace::GetStackWalkerFirstNonNullLoader (void) { - int trace_size = 32; + int trace_size = 100; _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 (); + StackWalkerNNLTraceData trace_data; + trace_data.expect = VMSW_GET_CALLING_ITEM; + trace_data.result = NULL; + state.trace_function = stackwalker_nnl_trace_fn; + state.trace_data = (void *) &trace_data; + + UpdateNCodeMap(); _Unwind_Backtrace (UnwindTraceFn, &state); - if (state.trace_data) - return (ClassLoader *) state.trace_data; - - return NULL; + return trace_data.result; }