--- old/src/os/posix/vm/os_posix.cpp 2016-07-31 23:33:18.198296151 -0700 +++ new/src/os/posix/vm/os_posix.cpp 2016-07-31 23:33:18.132295957 -0700 @@ -93,10 +93,6 @@ } int os::get_native_stack(address* stack, int frames, int toSkip) { -#ifdef _NMT_NOINLINE_ - toSkip++; -#endif - int frame_idx = 0; int num_of_frames; // number of frames captured frame fr = os::current_frame(); --- old/src/os/windows/vm/os_windows.cpp 2016-07-31 23:33:18.512297076 -0700 +++ new/src/os/windows/vm/os_windows.cpp 2016-07-31 23:33:18.442296870 -0700 @@ -318,9 +318,6 @@ // only supported on Windows XP or later. // int os::get_native_stack(address* stack, int frames, int toSkip) { -#ifdef _NMT_NOINLINE_ - toSkip++; -#endif int captured = RtlCaptureStackBackTrace(toSkip + 1, frames, (PVOID*)stack, NULL); for (int index = captured; index < frames; index ++) { stack[index] = NULL; --- old/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp 2016-07-31 23:33:18.812297960 -0700 +++ new/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp 2016-07-31 23:33:18.749297775 -0700 @@ -417,7 +417,15 @@ #else register intptr_t **ebp __asm__ (SPELL_REG_FP); #endif - return (intptr_t*) *ebp; // we want what it points to. + // ebp is for this frame (_get_previous_fp). We want the ebp for the + // caller of os::current_frame*(), so go up two frames. However, for + // optimized builds, _get_previous_fp() will be inlined, so only go + // up 1 frame in that case. +#ifdef _NMT_NOINLINE_ + return **(intptr_t***)ebp; +#else + return *ebp; +#endif } --- old/src/os_cpu/linux_x86/vm/os_linux_x86.cpp 2016-07-31 23:33:19.191299077 -0700 +++ new/src/os_cpu/linux_x86/vm/os_linux_x86.cpp 2016-07-31 23:33:19.126298872 -0700 @@ -235,7 +235,15 @@ #else register intptr_t **ebp __asm__ (SPELL_REG_FP); #endif - return (intptr_t*) *ebp; // we want what it points to. + // ebp is for this frame (_get_previous_fp). We want the ebp for the + // caller of os::current_frame*(), so go up two frames. However, for + // optimized builds, _get_previous_fp() will be inlined, so only go + // up 1 frame in that case. +#ifdef _NMT_NOINLINE_ + return **(intptr_t***)ebp; +#else + return *ebp; +#endif } --- old/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp 2016-07-31 23:33:19.459299867 -0700 +++ new/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp 2016-07-31 23:33:19.394299675 -0700 @@ -292,15 +292,19 @@ frame os::current_frame() { intptr_t* fp = _get_current_fp(); // it's inlined so want current fp + // fp is for os::current_frame. We want the fp for our caller. frame myframe((intptr_t*)os::current_stack_pointer(), (intptr_t*)fp, CAST_FROM_FN_PTR(address, os::current_frame)); - if (os::is_first_C_frame(&myframe)) { + frame caller_frame = os::get_sender_for_C_frame(&myframe); + + if (os::is_first_C_frame(&caller_frame)) { // stack is not walkable frame ret; // This will be a null useless frame return ret; } else { - return os::get_sender_for_C_frame(&myframe); + // return frame for our caller's caller + return os::get_sender_for_C_frame(&caller_frame); } } --- old/src/os_cpu/windows_x86/vm/os_windows_x86.cpp 2016-07-31 23:33:19.726300654 -0700 +++ new/src/os_cpu/windows_x86/vm/os_windows_x86.cpp 2016-07-31 23:33:19.662300465 -0700 @@ -496,7 +496,15 @@ __asm { mov frameptr, ebp }; + // ebp (frameptr) is for this frame (_get_previous_fp). We want the ebp for the + // caller of os::current_frame*(), so go up two frames. However, for + // optimized builds, _get_previous_fp() will be inlined, so only go + // up 1 frame in that case. +#ifdef _NMT_NOINLINE_ + return **(intptr_t***)frameptr; +#else return *frameptr; +#endif } #endif // !AMD64 --- old/src/share/vm/utilities/nativeCallStack.cpp 2016-07-31 23:33:19.975301387 -0700 +++ new/src/share/vm/utilities/nativeCallStack.cpp 2016-07-31 23:33:19.912301202 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2016, 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 @@ -33,6 +33,19 @@ _hash_value(0) { if (fillStack) { + // We need to skip the NativeCallStack::NativeCallStack frame if a tail call is NOT used + // to call os::get_native_stack. A tail call is used if _NMT_NOINLINE_ is not defined + // (which means this is not a slowdebug build), and we are on 64-bit (except Windows). + // This is not necessarily a rule, but what has been obvserved to date. +#define TAIL_CALL (!defined(_NMT_NOINLINE_) && !defined(WINDOWS) && defined(_LP64)) +#if !TAIL_CALL + toSkip++; +#if (defined(_NMT_NOINLINE_) && defined(BSD) && defined(_LP64)) + // Mac OS X slowdebug builds have this odd behavior where NativeCallStack::NativeCallStack + // appears as two frames, so we need to skip an extra frame. + toSkip++; +#endif +#endif os::get_native_stack(_stack, NMT_TrackingStackDepth, toSkip); } else { for (int index = 0; index < NMT_TrackingStackDepth; index ++) { --- /dev/null 2015-09-18 16:58:42.141718278 -0700 +++ new/test/runtime/NMT/CheckForProperDetailStackTrace.java 2016-07-31 23:33:20.149301900 -0700 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, 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. + */ + + /* + * @test + * @key nmt + * @summary Running with NMT detail should produce expected stack traces. + * @library /testlibrary + * @modules java.base/jdk.internal.misc + * java.management + */ + +import jdk.test.lib.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CheckForProperDetailStackTrace { + /* The stack trace we look for by default. Note that :: has been replaced by .* + to make sure it maches even if the symbol is not unmangled. */ + public static String stackTraceDefault = + ".*ModuleEntryTable.*new_entry.*\n" + + ".*ModuleEntryTable.*locked_create_entry_or_null.*\n" + + ".*Modules.*define_module.*\n" + + ".*JVM_DefineModule.*\n"; + + /* The stack trace we look for on Solaris and Windows slowdebug builds. For some + reason ALWAYSINLINE for AllocateHeap is ignored, so it appears in the stack strace. */ + public static String stackTraceAllocateHeap = + ".*AllocateHeap.*\n" + + ".*ModuleEntryTable.*new_entry.*\n" + + ".*ModuleEntryTable.*locked_create_entry_or_null.*\n" + + ".*Modules.*define_module.*\n"; + + /* A symbol that should always be present in NMT detail output. */ + private static String expectedSymbol = + "locked_create_entry_or_null"; + + private static final String jdkDebug = System.getProperty("jdk.debug"); + private static boolean isSlowDebugBuild() { + return (jdkDebug.toLowerCase().equals("slowdebug")); + } + + public static void main(String args[]) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:NativeMemoryTracking=detail", + "-XX:+PrintNMTStatistics", + "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldHaveExitValue(0); + + // We should never see either of these frames because they are supposed to be skipped. */ + output.shouldNotContain("NativeCallStack::NativeCallStack"); + output.shouldNotContain("os::get_native_stack"); + + // AllocateHeap shouldn't be in the output because it is suppose to always be inlined. + // We check for that here, but allow it for Windows and Solaris slowdebug builds because + // the compiler ends up not inlining AllocateHeap. + Boolean okToHaveAllocateHeap = + isSlowDebugBuild() && (Platform.isSolaris() || Platform.isWindows()); + if (!okToHaveAllocateHeap) { + output.shouldNotContain("AllocateHeap"); + } + + // See if we have any stack trace symbols in the output + boolean hasSymbols = + output.getStdout().contains(expectedSymbol) || output.getStderr().contains(expectedSymbol); + if (!hasSymbols) { + // It's ok for ARM not to have symbols, because it does not support NMT detail + // when targeting thumb2. It's also ok for Windows not to have symbols, because + // they are only available if the symbols file is included with the build. + if (Platform.isWindows() || Platform.isARM()) { + return; // we are done + } + output.reportDiagnosticSummary(); + throw new RuntimeException("Expected symbol missing missing from output: " + expectedSymbol); + } + + /* Make sure the expected NMT detail stack trace is found. */ + String expectedStackTrace = + (okToHaveAllocateHeap ? stackTraceAllocateHeap : stackTraceDefault); + if (!stackTraceMatches(expectedStackTrace, output)) { + output.reportDiagnosticSummary(); + throw new RuntimeException("Expected stack trace missing missing from output: " + expectedStackTrace); + } + } + + public static boolean stackTraceMatches(String stackTrace, OutputAnalyzer output) { + Matcher stdoutMatcher = Pattern.compile(stackTrace, Pattern.MULTILINE).matcher(output.getStdout()); + Matcher stderrMatcher = Pattern.compile(stackTrace, Pattern.MULTILINE).matcher(output.getStderr()); + return (stdoutMatcher.find() || stderrMatcher.find()); + } +}