Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 13
Don't preload jmethodIDs when cstack=vm with hotspot JVM#549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
5f5fa354e3bf98957d27c14728a764e183bc782c3cc462d857710c6f6c6f1a707c3a4c4827c683dda4e3a826e7e9d1818a13349c306b1a92b95c88cdf5e992c26aeeab2a9a1db56c6b4756790479adaea4702a624d6724c39149afed1c047b5a898f1ef3b88f7fe9bfaa92b9bb8a3e09396fec6bb03bac5b99ea12bf4732b41f1b3b524b81370e84d0154c295dc6ca79310d2c3d6b19fa5f1f704da9b1852f6f0e88be3be936d0face46bdf536281126a97db03f4a3d478735611b7baebb3213c7a00ae613222aecbf0ca9ea11a30e99a6adFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -253,6 +253,17 @@ Error Arguments::parse(const char *args) { | ||
| msg = "jstackdepth must be > 0"; | ||
| } | ||
| CASE("fjmethodid") | ||
| if (value != nullptr) { | ||
| if (strcmp(value, "false") == 0) { | ||
| _force_jmethodID = false; | ||
| } else if (strcmp(value, "true") == 0) { | ||
| _force_jmethodID = true; | ||
| } else { | ||
| msg = "Invalid jmethodID creation value"; | ||
| } | ||
| } | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| CASE("safemode") | ||
| _safe_mode = value == NULL ? INT_MAX : (int)strtol(value, NULL, 0); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -13,10 +13,11 @@ | ||
| #include "context_api.h" | ||
| #include "counters.h" | ||
| #include "dictionary.h" | ||
| #include "flightRecorder.h" | ||
| #include "flightRecorder.inline.h" | ||
| #include "incbin.h" | ||
| #include "jfrMetadata.h" | ||
| #include "jniHelper.h" | ||
| #include "jvmSupport.inline.h" | ||
| #include "os.h" | ||
| #include "profiler.h" | ||
| #include "signalSafety.h" | ||
| @@ -494,8 +495,12 @@ MethodInfo *Lookup::resolveMethod(ASGCT_CallFrame &frame) { | ||
| static const char* UNKNOWN = "unknown"; | ||
| unsigned long key; | ||
| jint bci = frame.bci; | ||
| jmethodID method_id = frame.method_id; | ||
| jmethodID method = frame.method_id; | ||
| // Resolve native method | ||
| if (FrameType::isRawPointer(bci)) { | ||
| method_id = JVMSupport::resolve(frame.method); | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| // BCI_VTABLE_RECEIVER: method holds a VMSymbol* (see vmEntry.h). Resolve | ||
| // to a class_id via the per-dump cache once, then key MethodMap by the | ||
| @@ -504,10 +509,10 @@ MethodInfo *Lookup::resolveMethod(ASGCT_CallFrame &frame) { | ||
| // row. | ||
| u32 vtable_class_id = 0; | ||
| if (bci == BCI_VTABLE_RECEIVER) { | ||
| vtable_class_id = resolveVTableReceiverCached((void *)method); | ||
| vtable_class_id = resolveVTableReceiverCached((void *)method_id); | ||
| } | ||
| if (method == nullptr) { | ||
| if (method_id == nullptr) { | ||
| key = MethodMap::makeKey(UNKNOWN); | ||
| } else if (bci == BCI_ERROR || bci == BCI_NATIVE_FRAME) { | ||
| key = MethodMap::makeKey(frame.native_function_name); | ||
| @@ -520,7 +525,7 @@ MethodInfo *Lookup::resolveMethod(ASGCT_CallFrame &frame) { | ||
| assert(frame_type == FRAME_INTERPRETED || frame_type == FRAME_JIT_COMPILED || | ||
| frame_type == FRAME_INLINED || frame_type == FRAME_C1_COMPILED || | ||
| VM::isOpenJ9()); // OpenJ9 may have bugs that produce invalid frame types | ||
| key = MethodMap::makeKey(method); | ||
| key = MethodMap::makeKey(method_id); | ||
| } | ||
| MethodInfo *mi = &(*_method_map)[key]; | ||
| @@ -536,12 +541,12 @@ MethodInfo *Lookup::resolveMethod(ASGCT_CallFrame &frame) { | ||
| // (PROF-15130). The allocator recycles ids freed on erase instead. | ||
| mi->_key = _method_map->allocId(); | ||
| } | ||
| if (method == nullptr) { | ||
| if (method_id == nullptr) { | ||
| fillNativeMethodInfo(mi, UNKNOWN, nullptr); | ||
| } else if (bci == BCI_ERROR) { | ||
| fillNativeMethodInfo(mi, (const char *)method, nullptr); | ||
| fillNativeMethodInfo(mi, (const char *)method_id, nullptr); | ||
| } else if (bci == BCI_NATIVE_FRAME) { | ||
| const char *name = (const char *)method; | ||
| const char *name = (const char *)method_id; | ||
| fillNativeMethodInfo(mi, name, | ||
| Profiler::instance()->getLibraryName(name)); | ||
| } else if (bci == BCI_NATIVE_FRAME_REMOTE) { | ||
| @@ -589,7 +594,7 @@ MethodInfo *Lookup::resolveMethod(ASGCT_CallFrame &frame) { | ||
| mi->_type = FRAME_NATIVE; | ||
| mi->_is_entry = false; | ||
| } else { | ||
| fillJavaMethodInfo(mi, method, first_time); | ||
| fillJavaMethodInfo(mi, method_id, first_time); | ||
| } | ||
| } | ||
| @@ -1574,7 +1579,7 @@ int Recording::writeStackTraces(Buffer *buf, Lookup *lookup) { | ||
| jint bci = trace->frames[i].bci; | ||
| if (mi->_type < FRAME_NATIVE) { | ||
| FrameTypeId type = FrameType::decode(bci); | ||
| bci = (bci & 0x10000) ? 0 : (bci & 0xffff); | ||
| bci = FrameType::bci(bci); | ||
| buf->putVar32(mi->getLineNumber(bci)); | ||
| buf->putVar32(bci); | ||
| buf->put8(type); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /* | ||
| * Copyright The async-profiler authors | ||
| * Copyright 2026, Datadog, Inc. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #ifndef _FLIGHTRECORDER_INLINE_H | ||
| #define _FLIGHTRECORDER_INLINE_H | ||
| #include "flightRecorder.h" | ||
| #include "jvmSupport.inline.h" | ||
| jint MethodInfo::getLineNumber(jint bci) { | ||
| // if the shared pointer is not pointing to the line number table, consider | ||
| // size 0 | ||
| if (!_line_number_table || _line_number_table->_size == 0) { | ||
| return 0; | ||
| } | ||
| int i = 1; | ||
| while (i < _line_number_table->_size && | ||
| bci >= ((jvmtiLineNumberEntry *)_line_number_table->_ptr)[i] | ||
| .start_location) { | ||
| i++; | ||
| } | ||
| return ((jvmtiLineNumberEntry *)_line_number_table->_ptr)[i - 1] | ||
| .line_number; | ||
| } | ||
| bool MethodInfo::isHidden() { | ||
| return JVMSupport::isHidden(_modifiers); | ||
| } | ||
| #endif // _FLIGHTRECORDER_INLINE_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,24 @@ | ||
| /* | ||
| * Copyright 2026 Datadog, Inc | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #ifndef _FRAME_H | ||
| #define _FRAME_H | ||
| #include <cassert> | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| #include "vmEntry.h" | ||
| enum FrameTypeId { | ||
| FRAME_INTERPRETED = 0, | ||
| FRAME_JIT_COMPILED = 1, | ||
| @@ -10,23 +28,59 @@ enum FrameTypeId { | ||
| FRAME_KERNEL = 5, | ||
| FRAME_C1_COMPILED = 6, | ||
| FRAME_NATIVE_REMOTE = 7, // Native frame with remote symbolication (build-id + pc-offset) | ||
| FRAME_TYPE_MAX = FRAME_NATIVE_REMOTE // Maximum valid frame type | ||
| FRAME_TYPE_MAX = FRAME_NATIVE_REMOTE, // Maximum valid frame type | ||
| FRAME_TYPE_MASK = 0x7 | ||
| }; | ||
| // Packs frame type and BCI into a single int field stored in CallTrace frames. | ||
| // | ||
| // Bit layout of an encoded value (ENCODED_MASK set): | ||
| // bit 30 RAW_POINTER_MASK — value is a raw native PC (HotSpot only) | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // bits 23–21 frame type — FrameTypeId (0–7) | ||
| // bit 20 ENCODED_MASK — set to distinguish encoded values from raw ASGCT BCIs | ||
| // bits 15–0 BCI — bytecode index (0–65534; never 65535, see BCI_MASK) | ||
| // | ||
| // When ENCODED_MASK is not set the field is a raw, unencoded BCI from ASGCT (non-VM stack | ||
| // walking modes). Raw values may be negative: HotSpot uses -1 as a sentinel for method-entry | ||
| // and synchronization-entry samples. Both encode() and bci() clamp negative values to 0 so | ||
| // that 65535 (the mask value itself) is never emitted and can be treated as unreachable. | ||
| // decode() returns FRAME_JIT_COMPILED for all unencoded or negative values. | ||
| class FrameType { | ||
| // JVM spec §4.7.3 caps method bytecode at 65535 bytes, so valid BCIs are 0–65534. | ||
| // The mask value 65535 (0xffff) is therefore never a valid BCI; we keep it unreachable | ||
| // by clamping negative sentinels to 0 in encode() and bci(). | ||
| static constexpr int BCI_MASK = 0xffff; | ||
| static constexpr int TYPE_SHIFT = 21; | ||
| static constexpr int ENCODED_MASK = 1 << 20; // distinguishes encoded values from raw ASGCT BCIs | ||
| static constexpr int RAW_POINTER_MASK = 1 << 30; | ||
zhengyu123 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| public: | ||
| static inline int encode(int type, int bci) { | ||
| return (1 << 24) | (type << 25) | (bci & 0xffffff); | ||
| // Produces an encoded int from a frame type and BCI. Negative BCIs (HotSpot -1 sentinels | ||
| // for method-entry/sync-entry) are mapped to 0 rather than wrapping to 0xffff. | ||
| static inline int encode(int type, int bci, bool rawPointer = false) { | ||
| assert((!rawPointer || VM::isHotspot()) && "Raw pointer is only valid for hotspot"); | ||
| assert(type >= FRAME_INTERPRETED && type <= FRAME_TYPE_MAX); | ||
| int bci_bits = (bci < 0) ? 0 : (bci & BCI_MASK); | ||
| return ENCODED_MASK | (type << TYPE_SHIFT) | bci_bits | (rawPointer ? RAW_POINTER_MASK : 0); | ||
| } | ||
| // Extracts the BCI from either an encoded value or a raw ASGCT BCI. | ||
| // Negative values (HotSpot -1 sentinels) are clamped to 0, matching encode(). | ||
| static inline int bci(int bci) { | ||
| return (bci < 0) ? 0 : (bci & BCI_MASK); | ||
| } | ||
| // Extracts the FrameTypeId from an encoded value. | ||
| // Returns FRAME_JIT_COMPILED for unencoded raw ASGCT BCIs and for negative sentinels, | ||
| // since no type information is available in those cases. | ||
| static inline FrameTypeId decode(int bci) { | ||
| if ((bci >> 24) <= 0) { | ||
| // Unencoded BCI (bit 24 not set) or negative special BCI values | ||
| if ((bci & ENCODED_MASK) == 0 || bci < 0) { | ||
| return FRAME_JIT_COMPILED; | ||
| } | ||
| // Clamp to valid FrameTypeId range to defend against corrupted values | ||
| int raw_type = bci >> 25; | ||
| return (FrameTypeId)(raw_type <= FRAME_TYPE_MAX ? raw_type : FRAME_TYPE_MAX); | ||
| return (FrameTypeId)((bci >> TYPE_SHIFT) & FRAME_TYPE_MASK); | ||
| } | ||
| static inline bool isRawPointer(int bci) { | ||
| return bci > 0 && (bci & RAW_POINTER_MASK) != 0; | ||
| } | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.