Created
March 17, 2026 11:13
-
-
Save qwwdfsad/c6e4f9530bae4c87ee6f795e6c4b62f6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Subject: [PATCH] JSR-45 support | |
| --- | |
| Index: src/hotspot/share/oops/instanceKlass.hpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp | |
| --- a/src/hotspot/share/oops/instanceKlass.hpp (revision cb059a6b1b1686b7adcb2e88536060f4f7d47118) | |
| +++ b/src/hotspot/share/oops/instanceKlass.hpp (date 1772629314300) | |
| @@ -46,6 +46,7 @@ | |
| class DeoptimizationScope; | |
| class klassItable; | |
| class RecordComponent; | |
| +class SourceMapInfo; | |
| // An InstanceKlass is the VM level representation of a Java class. | |
| // It contains all information needed for at class at execution runtime. | |
| @@ -209,6 +210,10 @@ | |
| // it is stored in the instanceklass as a null-terminated UTF-8 string | |
| const char* _source_debug_extension; | |
| + // Lazily parsed SMAP data from _source_debug_extension. | |
| + // Initialized on first use via Atomic::cmpxchg. Owned by this klass. | |
| + SourceMapInfo* _source_map_info; | |
| + | |
| // Number of heapOopSize words used by non-static fields in this klass | |
| // (including inherited fields but after header_size()). | |
| int _nonstatic_field_size; | |
| @@ -686,6 +691,9 @@ | |
| const char* source_debug_extension() const { return _source_debug_extension; } | |
| void set_source_debug_extension(const char* array, int length); | |
| + // Lazily-initialized parsed SMAP. Returns nullptr if no valid SMAP. | |
| + SourceMapInfo* source_map_info(); | |
| + | |
| // nonstatic oop-map blocks | |
| static int nonstatic_oop_map_size(unsigned int oop_map_count) { | |
| return oop_map_count * OopMapBlock::size_in_words(); | |
| Index: src/hotspot/share/oops/instanceKlass.cpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp | |
| --- a/src/hotspot/share/oops/instanceKlass.cpp (revision cb059a6b1b1686b7adcb2e88536060f4f7d47118) | |
| +++ b/src/hotspot/share/oops/instanceKlass.cpp (date 1772718701156) | |
| @@ -40,6 +40,7 @@ | |
| #include "classfile/verifier.hpp" | |
| #include "classfile/vmClasses.hpp" | |
| #include "classfile/vmSymbols.hpp" | |
| +#include "classfile/sourceDebugExtension.hpp" | |
| #include "code/codeCache.hpp" | |
| #include "code/dependencyContext.hpp" | |
| #include "compiler/compilationPolicy.hpp" | |
| @@ -2746,6 +2747,7 @@ | |
| // These are not allocated from metaspace. They are safe to set to null. | |
| _source_debug_extension = nullptr; | |
| + _source_map_info = nullptr; | |
| _dep_context = nullptr; | |
| _osr_nmethods_head = nullptr; | |
| #if INCLUDE_JVMTI | |
| @@ -2989,6 +2991,8 @@ | |
| #endif | |
| FREE_C_HEAP_ARRAY(char, _source_debug_extension); | |
| + delete _source_map_info; | |
| + _source_map_info = nullptr; | |
| if (release_sub_metadata) { | |
| constants()->release_C_heap_structures(); | |
| @@ -3019,6 +3023,33 @@ | |
| } | |
| return nullptr; | |
| } | |
| + | |
| +SourceMapInfo* InstanceKlass::source_map_info() { | |
| + // TODO race? | |
| + SourceMapInfo* info = _source_map_info; | |
| + if (info != nullptr) { | |
| + return info; | |
| + } | |
| + const char* sde = _source_debug_extension; | |
| + if (sde == nullptr) { | |
| + return nullptr; | |
| + } | |
| + // Parse on first access. Benign race: if two threads parse simultaneously, | |
| + // one result is discarded. The field is only written once (null -> valid). | |
| + // TODO slow if corrupted | |
| + // TODO can be even allocated directly? | |
| + info = new SourceMapInfo(sde); | |
| + if (!info->is_valid()) { | |
| + delete info; | |
| + return nullptr; | |
| + } | |
| + if (AtomicAccess::replace_if_null(&_source_map_info, info)) { | |
| + return info; | |
| + } | |
| + // Another thread won the race | |
| + delete info; | |
| + return _source_map_info; | |
| +} | |
| void InstanceKlass::set_source_debug_extension(const char* array, int length) { | |
| if (array == nullptr) { | |
| Index: src/hotspot/share/classfile/sourceDebugExtension.hpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/classfile/sourceDebugExtension.hpp b/src/hotspot/share/classfile/sourceDebugExtension.hpp | |
| new file mode 100644 | |
| --- /dev/null (date 1772628705444) | |
| +++ b/src/hotspot/share/classfile/sourceDebugExtension.hpp (date 1772628705444) | |
| @@ -0,0 +1,136 @@ | |
| +/* | |
| + * Copyright (c) 2025, 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. | |
| + */ | |
| + | |
| +#ifndef SHARE_CLASSFILE_SOURCEDEBUGEXTENSION_HPP | |
| +#define SHARE_CLASSFILE_SOURCEDEBUGEXTENSION_HPP | |
| + | |
| +#include "memory/allocation.hpp" | |
| + | |
| +// Parsed representation of a JSR-45 SourceDebugExtension (SMAP) attribute. | |
| +// Used to translate Java line numbers and file names in stack traces | |
| +// back to original source locations (e.g., Kotlin inline/suspend functions). | |
| +// | |
| +// Thread-safe: instances are immutable after construction. | |
| +// Allocated on C-heap and cached on InstanceKlass. | |
| + | |
| +class SourceMapInfo : public CHeapObj<mtClass> { | |
| + public: | |
| + struct FileEntry { | |
| + int file_id; | |
| + char* source_name; // allocated, owned | |
| + char* source_path; // allocated, owned (may be nullptr) | |
| + }; | |
| + | |
| + struct LineEntry { | |
| + int jpls_start; // Java line range start | |
| + int jpls_end; // Java line range end (inclusive) | |
| + int jpls_line_inc; // Java line increment | |
| + int njpls_start; // Original source line start | |
| + int njpls_end; // Original source line end (inclusive) | |
| + int file_id; // Reference to FileEntry | |
| + }; | |
| + | |
| + struct StratumEntry { | |
| + char* id; // stratum name (allocated, owned) | |
| + int file_index; // start index in _files array | |
| + int line_index; // start index in _lines array | |
| + }; | |
| + | |
| + private: | |
| + bool _valid; | |
| + | |
| + FileEntry* _files; | |
| + int _file_count; | |
| + | |
| + LineEntry* _lines; | |
| + int _line_count; | |
| + | |
| + StratumEntry* _strata; | |
| + int _stratum_count; | |
| + | |
| + int _default_stratum_index; | |
| + int _base_stratum_index; | |
| + | |
| + // Parser state (only used during construction) | |
| + const char* _sde; // input string (not owned) | |
| + const char* _pos; // current parse position | |
| + int _current_file_id; | |
| + bool _parse_error; | |
| + | |
| + // Growable arrays during parsing | |
| + int _files_capacity; | |
| + int _lines_capacity; | |
| + int _strata_capacity; | |
| + | |
| + // Parsing methods | |
| + void decode(); | |
| + char peek(); | |
| + char read(); | |
| + void advance(); | |
| + void ignore_white(); | |
| + void ignore_line(); | |
| + int read_number(); | |
| + char* read_line(); // returns allocated copy | |
| + void syntax_error(); | |
| + | |
| + void create_java_stratum(const char* jpls_filename); | |
| + void stratum_section(); | |
| + void file_section(); | |
| + void line_section(); | |
| + void ignore_section(); | |
| + void file_line(); | |
| + void line_line(); | |
| + | |
| + void store_stratum(const char* id); | |
| + void store_file(int file_id, const char* source_name, const char* source_path); | |
| + void store_line(int jpls_start, int jpls_end, int jpls_line_inc, | |
| + int njpls_start, int njpls_end, int file_id); | |
| + | |
| + void ensure_file_capacity(); | |
| + void ensure_line_capacity(); | |
| + void ensure_stratum_capacity(); | |
| + | |
| + // Query helpers | |
| + int default_stratum_table_index() const; | |
| + int sti_line_table_index(int sti, int jpls_line) const; | |
| + int sti_line_number(int sti, int lti, int jpls_line) const; | |
| + int sti_file_id(int lti) const; | |
| + int find_file_index(int sti, int file_id) const; | |
| + | |
| + public: | |
| + // Parse an SMAP string. Returns a valid SourceMapInfo on success, | |
| + // or one with is_valid()==false on parse error. | |
| + SourceMapInfo(const char* source_debug_extension); | |
| + ~SourceMapInfo(); | |
| + | |
| + bool is_valid() const { return _valid; } | |
| + | |
| + // Result of a line translation query. | |
| + struct TranslationResult { | |
| + bool translated; // true if a mapping was found | |
| + int line_number; // translated line number | |
| + const char* source_name; // translated source file name (not owned, valid for lifetime of SourceMapInfo) | |
| + }; | |
| + | |
| + // Translate a Java line number using the default stratum. | |
| + // If no mapping exists, returns result with translated==false. | |
| + TranslationResult translate(int java_line_number) const; | |
| +}; | |
| + | |
| +#endif // SHARE_CLASSFILE_SOURCEDEBUGEXTENSION_HPP | |
| Index: src/hotspot/share/classfile/sourceDebugExtension.cpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/classfile/sourceDebugExtension.cpp b/src/hotspot/share/classfile/sourceDebugExtension.cpp | |
| new file mode 100644 | |
| --- /dev/null (date 1772628757590) | |
| +++ b/src/hotspot/share/classfile/sourceDebugExtension.cpp (date 1772628757590) | |
| @@ -0,0 +1,482 @@ | |
| +/* | |
| + * Copyright (c) 2025, 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. | |
| + */ | |
| + | |
| +#include "classfile/sourceDebugExtension.hpp" | |
| +#include "memory/allocation.hpp" | |
| +#include "utilities/copy.hpp" | |
| + | |
| +#include <string.h> | |
| + | |
| +// Initial capacities for growable arrays during parsing | |
| +static const int INIT_FILE_CAPACITY = 10; | |
| +static const int INIT_LINE_CAPACITY = 100; | |
| +static const int INIT_STRATUM_CAPACITY = 3; | |
| + | |
| +static char* alloc_string(const char* src) { | |
| + if (src == nullptr) return nullptr; | |
| + size_t len = strlen(src); | |
| + char* dst = NEW_C_HEAP_ARRAY(char, len + 1, mtClass); | |
| + memcpy(dst, src, len + 1); | |
| + return dst; | |
| +} | |
| + | |
| +static char* alloc_string(const char* src, size_t len) { | |
| + char* dst = NEW_C_HEAP_ARRAY(char, len + 1, mtClass); | |
| + memcpy(dst, src, len); | |
| + dst[len] = '\0'; | |
| + return dst; | |
| +} | |
| + | |
| +// ============================================================================ | |
| +// Construction / Destruction | |
| +// ============================================================================ | |
| + | |
| +SourceMapInfo::SourceMapInfo(const char* source_debug_extension) : | |
| + _valid(false), | |
| + _files(nullptr), | |
| + _file_count(0), | |
| + _lines(nullptr), | |
| + _line_count(0), | |
| + _strata(nullptr), | |
| + _stratum_count(0), | |
| + _default_stratum_index(-1), | |
| + _base_stratum_index(-2), | |
| + _sde(source_debug_extension), | |
| + _pos(source_debug_extension), | |
| + _current_file_id(0), | |
| + _parse_error(false), | |
| + _files_capacity(0), | |
| + _lines_capacity(0), | |
| + _strata_capacity(0) | |
| +{ | |
| + if (_sde != nullptr && strlen(_sde) > 4) { | |
| + decode(); | |
| + } | |
| +} | |
| + | |
| +SourceMapInfo::~SourceMapInfo() { | |
| + for (int i = 0; i < _file_count; i++) { | |
| + FREE_C_HEAP_ARRAY(char, _files[i].source_name); | |
| + FREE_C_HEAP_ARRAY(char, _files[i].source_path); | |
| + } | |
| + for (int i = 0; i < _stratum_count; i++) { | |
| + FREE_C_HEAP_ARRAY(char, _strata[i].id); | |
| + } | |
| + FREE_C_HEAP_ARRAY(FileEntry, _files); | |
| + FREE_C_HEAP_ARRAY(LineEntry, _lines); | |
| + FREE_C_HEAP_ARRAY(StratumEntry, _strata); | |
| +} | |
| + | |
| +// ============================================================================ | |
| +// Parser - low level | |
| +// ============================================================================ | |
| + | |
| +void SourceMapInfo::syntax_error() { | |
| + _parse_error = true; | |
| +} | |
| + | |
| +char SourceMapInfo::peek() { | |
| + if (_parse_error || *_pos == '\0') { | |
| + syntax_error(); | |
| + return '\0'; | |
| + } | |
| + return *_pos; | |
| +} | |
| + | |
| +char SourceMapInfo::read() { | |
| + if (_parse_error || *_pos == '\0') { | |
| + syntax_error(); | |
| + return '\0'; | |
| + } | |
| + return *_pos++; | |
| +} | |
| + | |
| +void SourceMapInfo::advance() { | |
| + if (!_parse_error && *_pos != '\0') { | |
| + _pos++; | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::ignore_white() { | |
| + while (!_parse_error && (*_pos == ' ' || *_pos == '\t')) { | |
| + _pos++; | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::ignore_line() { | |
| + if (_parse_error) return; | |
| + char ch; | |
| + do { | |
| + ch = read(); | |
| + if (_parse_error) return; | |
| + } while (ch != '\n' && ch != '\r'); | |
| + | |
| + // Check for CR LF | |
| + if (ch == '\r' && *_pos == '\n') { | |
| + _pos++; | |
| + } | |
| + ignore_white(); | |
| +} | |
| + | |
| +int SourceMapInfo::read_number() { | |
| + int value = 0; | |
| + ignore_white(); | |
| + if (_parse_error) return 0; | |
| + while (*_pos >= '0' && *_pos <= '9') { | |
| + value = (value * 10) + (*_pos - '0'); | |
| + _pos++; | |
| + } | |
| + ignore_white(); | |
| + return value; | |
| +} | |
| + | |
| +// Returns an allocated copy of the line content. | |
| +char* SourceMapInfo::read_line() { | |
| + ignore_white(); | |
| + if (_parse_error) return alloc_string(""); | |
| + | |
| + const char* start = _pos; | |
| + while (*_pos != '\n' && *_pos != '\r' && *_pos != '\0') { | |
| + _pos++; | |
| + } | |
| + size_t len = (size_t)(_pos - start); | |
| + | |
| + // Consume line ending | |
| + if (*_pos == '\r') { | |
| + _pos++; | |
| + if (*_pos == '\n') _pos++; | |
| + } else if (*_pos == '\n') { | |
| + _pos++; | |
| + } | |
| + ignore_white(); | |
| + | |
| + return alloc_string(start, len); | |
| +} | |
| + | |
| +// ============================================================================ | |
| +// Parser - storage | |
| +// ============================================================================ | |
| + | |
| +void SourceMapInfo::ensure_file_capacity() { | |
| + if (_file_count >= _files_capacity) { | |
| + int new_cap = (_files_capacity == 0) ? INIT_FILE_CAPACITY : _files_capacity * 2; | |
| + FileEntry* new_arr = NEW_C_HEAP_ARRAY(FileEntry, new_cap, mtClass); | |
| + if (_files != nullptr) { | |
| + memcpy(new_arr, _files, _file_count * sizeof(FileEntry)); | |
| + FREE_C_HEAP_ARRAY(FileEntry, _files); | |
| + } | |
| + _files = new_arr; | |
| + _files_capacity = new_cap; | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::ensure_line_capacity() { | |
| + if (_line_count >= _lines_capacity) { | |
| + int new_cap = (_lines_capacity == 0) ? INIT_LINE_CAPACITY : _lines_capacity * 2; | |
| + LineEntry* new_arr = NEW_C_HEAP_ARRAY(LineEntry, new_cap, mtClass); | |
| + if (_lines != nullptr) { | |
| + memcpy(new_arr, _lines, _line_count * sizeof(LineEntry)); | |
| + FREE_C_HEAP_ARRAY(LineEntry, _lines); | |
| + } | |
| + _lines = new_arr; | |
| + _lines_capacity = new_cap; | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::ensure_stratum_capacity() { | |
| + if (_stratum_count >= _strata_capacity) { | |
| + int new_cap = (_strata_capacity == 0) ? INIT_STRATUM_CAPACITY : _strata_capacity * 2; | |
| + StratumEntry* new_arr = NEW_C_HEAP_ARRAY(StratumEntry, new_cap, mtClass); | |
| + if (_strata != nullptr) { | |
| + memcpy(new_arr, _strata, _stratum_count * sizeof(StratumEntry)); | |
| + FREE_C_HEAP_ARRAY(StratumEntry, _strata); | |
| + } | |
| + _strata = new_arr; | |
| + _strata_capacity = new_cap; | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::store_file(int file_id, const char* source_name, const char* source_path) { | |
| + ensure_file_capacity(); | |
| + _files[_file_count].file_id = file_id; | |
| + _files[_file_count].source_name = alloc_string(source_name); | |
| + _files[_file_count].source_path = (source_path != nullptr) ? alloc_string(source_path) : nullptr; | |
| + _file_count++; | |
| +} | |
| + | |
| +void SourceMapInfo::store_line(int jpls_start, int jpls_end, int jpls_line_inc, | |
| + int njpls_start, int njpls_end, int file_id) { | |
| + ensure_line_capacity(); | |
| + _lines[_line_count].jpls_start = jpls_start; | |
| + _lines[_line_count].jpls_end = jpls_end; | |
| + _lines[_line_count].jpls_line_inc = jpls_line_inc; | |
| + _lines[_line_count].njpls_start = njpls_start; | |
| + _lines[_line_count].njpls_end = njpls_end; | |
| + _lines[_line_count].file_id = file_id; | |
| + _line_count++; | |
| +} | |
| + | |
| +void SourceMapInfo::store_stratum(const char* id) { | |
| + // Remove redundant strata (nothing changed since last store) | |
| + if (_stratum_count > 0) { | |
| + if (_strata[_stratum_count - 1].file_index == _file_count && | |
| + _strata[_stratum_count - 1].line_index == _line_count) { | |
| + FREE_C_HEAP_ARRAY(char, _strata[_stratum_count - 1].id); | |
| + _stratum_count--; | |
| + } | |
| + } | |
| + ensure_stratum_capacity(); | |
| + _strata[_stratum_count].id = alloc_string(id); | |
| + _strata[_stratum_count].file_index = _file_count; | |
| + _strata[_stratum_count].line_index = _line_count; | |
| + _stratum_count++; | |
| + _current_file_id = 0; | |
| +} | |
| + | |
| +// ============================================================================ | |
| +// Parser - sections | |
| +// ============================================================================ | |
| + | |
| +void SourceMapInfo::create_java_stratum(const char* jpls_filename) { | |
| + _base_stratum_index = _stratum_count; | |
| + store_stratum("Java"); | |
| + store_file(1, jpls_filename, nullptr); | |
| + // Java line numbers cannot exceed 65535 | |
| + store_line(1, 65536, 1, 1, 65536, 1); | |
| + store_stratum("Aux"); // sentinel in case no strata declared | |
| +} | |
| + | |
| +void SourceMapInfo::file_line() { | |
| + if (_parse_error) return; | |
| + | |
| + bool has_absolute = false; | |
| + if (peek() == '+') { | |
| + advance(); | |
| + has_absolute = true; | |
| + } | |
| + int file_id = read_number(); | |
| + char* source_name = read_line(); | |
| + char* source_path = nullptr; | |
| + if (has_absolute) { | |
| + source_path = read_line(); | |
| + } | |
| + store_file(file_id, source_name, source_path); | |
| + FREE_C_HEAP_ARRAY(char, source_name); | |
| + FREE_C_HEAP_ARRAY(char, source_path); | |
| +} | |
| + | |
| +// Parse: <NJ-start-line> [ # <file-id> ] [ , <line-count> ] : <J-start-line> [ , <line-increment> ] | |
| +void SourceMapInfo::line_line() { | |
| + if (_parse_error) return; | |
| + | |
| + int line_count = 1; | |
| + int line_increment = 1; | |
| + | |
| + int njpls_start = read_number(); | |
| + | |
| + if (!_parse_error && peek() == '#') { | |
| + advance(); | |
| + _current_file_id = read_number(); | |
| + } | |
| + | |
| + if (!_parse_error && peek() == ',') { | |
| + advance(); | |
| + line_count = read_number(); | |
| + } | |
| + | |
| + if (_parse_error || read() != ':') { | |
| + syntax_error(); | |
| + return; | |
| + } | |
| + | |
| + int jpls_start = read_number(); | |
| + | |
| + if (!_parse_error && peek() == ',') { | |
| + advance(); | |
| + line_increment = read_number(); | |
| + } | |
| + | |
| + ignore_line(); | |
| + | |
| + if (!_parse_error) { | |
| + store_line(jpls_start, | |
| + jpls_start + (line_count * line_increment) - 1, | |
| + line_increment, | |
| + njpls_start, | |
| + njpls_start + line_count - 1, | |
| + _current_file_id); | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::file_section() { | |
| + ignore_line(); | |
| + while (!_parse_error && peek() != '*') { | |
| + file_line(); | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::line_section() { | |
| + ignore_line(); | |
| + while (!_parse_error && peek() != '*') { | |
| + line_line(); | |
| + } | |
| +} | |
| + | |
| +void SourceMapInfo::stratum_section() { | |
| + char* id = read_line(); | |
| + store_stratum(id); | |
| + FREE_C_HEAP_ARRAY(char, id); | |
| +} | |
| + | |
| +void SourceMapInfo::ignore_section() { | |
| + ignore_line(); | |
| + while (!_parse_error && peek() != '*') { | |
| + ignore_line(); | |
| + } | |
| +} | |
| + | |
| +// ============================================================================ | |
| +// Parser - main entry | |
| +// ============================================================================ | |
| + | |
| +void SourceMapInfo::decode() { | |
| + // Check for "SMAP" header | |
| + if (read() != 'S' || read() != 'M' || read() != 'A' || read() != 'P') { | |
| + return; // not SMAP data | |
| + } | |
| + if (_parse_error) return; | |
| + | |
| + ignore_line(); // rest of SMAP line | |
| + | |
| + char* jpls_filename = read_line(); | |
| + char* default_stratum_id = read_line(); | |
| + | |
| + create_java_stratum(jpls_filename); | |
| + | |
| + while (!_parse_error) { | |
| + if (read() != '*') { | |
| + syntax_error(); | |
| + break; | |
| + } | |
| + char section = read(); | |
| + if (_parse_error) break; | |
| + | |
| + switch (section) { | |
| + case 'S': | |
| + stratum_section(); | |
| + break; | |
| + case 'F': | |
| + file_section(); | |
| + break; | |
| + case 'L': | |
| + line_section(); | |
| + break; | |
| + case 'E': | |
| + // End marker - finalize | |
| + store_stratum("*terminator*"); | |
| + _valid = true; | |
| + // Resolve default stratum index | |
| + _default_stratum_index = -1; | |
| + for (int i = 0; i < _stratum_count - 1; i++) { | |
| + if (strcmp(_strata[i].id, default_stratum_id) == 0) { | |
| + _default_stratum_index = i; | |
| + break; | |
| + } | |
| + } | |
| + FREE_C_HEAP_ARRAY(char, jpls_filename); | |
| + FREE_C_HEAP_ARRAY(char, default_stratum_id); | |
| + return; | |
| + default: | |
| + ignore_section(); | |
| + break; | |
| + } | |
| + } | |
| + | |
| + // Parse error path | |
| + FREE_C_HEAP_ARRAY(char, jpls_filename); | |
| + FREE_C_HEAP_ARRAY(char, default_stratum_id); | |
| +} | |
| + | |
| +// ============================================================================ | |
| +// Query methods | |
| +// ============================================================================ | |
| + | |
| +int SourceMapInfo::default_stratum_table_index() const { | |
| + return _default_stratum_index; | |
| +} | |
| + | |
| +int SourceMapInfo::sti_line_table_index(int sti, int jpls_line) const { | |
| + int line_start = _strata[sti].line_index; | |
| + int line_end = _strata[sti + 1].line_index; // one past end (guaranteed by terminator stratum) | |
| + for (int i = line_start; i < line_end; i++) { | |
| + if (jpls_line >= _lines[i].jpls_start && jpls_line <= _lines[i].jpls_end) { | |
| + return i; | |
| + } | |
| + } | |
| + return -1; | |
| +} | |
| + | |
| +int SourceMapInfo::sti_line_number(int sti, int lti, int jpls_line) const { | |
| + return _lines[lti].njpls_start + | |
| + ((jpls_line - _lines[lti].jpls_start) / _lines[lti].jpls_line_inc); | |
| +} | |
| + | |
| +int SourceMapInfo::sti_file_id(int lti) const { | |
| + return _lines[lti].file_id; | |
| +} | |
| + | |
| +int SourceMapInfo::find_file_index(int sti, int file_id) const { | |
| + int file_start = _strata[sti].file_index; | |
| + int file_end = _strata[sti + 1].file_index; | |
| + for (int i = file_start; i < file_end; i++) { | |
| + if (_files[i].file_id == file_id) { | |
| + return i; | |
| + } | |
| + } | |
| + return -1; | |
| +} | |
| + | |
| +SourceMapInfo::TranslationResult SourceMapInfo::translate(int java_line_number) const { | |
| + TranslationResult result = { false, java_line_number, nullptr }; | |
| + | |
| + if (!_valid) return result; | |
| + | |
| + int sti = default_stratum_table_index(); | |
| + if (sti < 0 || sti == _base_stratum_index) { | |
| + // No default stratum or default is Java — no translation | |
| + return result; | |
| + } | |
| + | |
| + int lti = sti_line_table_index(sti, java_line_number); | |
| + if (lti < 0) { | |
| + // No mapping for this line | |
| + return result; | |
| + } | |
| + | |
| + result.translated = true; | |
| + result.line_number = sti_line_number(sti, lti, java_line_number); | |
| + | |
| + int file_id = sti_file_id(lti); | |
| + int fi = find_file_index(sti, file_id); | |
| + if (fi >= 0) { | |
| + result.source_name = _files[fi].source_name; | |
| + } | |
| + | |
| + return result; | |
| +} | |
| Index: src/hotspot/share/classfile/javaClasses.cpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp | |
| --- a/src/hotspot/share/classfile/javaClasses.cpp (revision cb059a6b1b1686b7adcb2e88536060f4f7d47118) | |
| +++ b/src/hotspot/share/classfile/javaClasses.cpp (date 1772629452697) | |
| @@ -38,6 +38,7 @@ | |
| #include "classfile/systemDictionary.hpp" | |
| #include "classfile/vmClasses.hpp" | |
| #include "classfile/vmSymbols.hpp" | |
| +#include "classfile/sourceDebugExtension.hpp" | |
| #include "code/debugInfo.hpp" | |
| #include "code/dependencyContext.hpp" | |
| #include "code/pcDesc.hpp" | |
| @@ -3152,6 +3153,21 @@ | |
| } | |
| } | |
| line_number = Backtrace::get_line_number(method(), bci); | |
| + | |
| + // Translate through JSR-45 SMAP if available and enabled | |
| + if (TranslateSourceDebugExtension && line_number > 0) { | |
| + SourceMapInfo* smap = holder->source_map_info(); | |
| + if (smap != nullptr) { | |
| + SourceMapInfo::TranslationResult tr = smap->translate(line_number); | |
| + if (tr.translated) { | |
| + line_number = tr.line_number; | |
| + if (tr.source_name != nullptr) { | |
| + // Override the source file name with the SMAP-translated one | |
| + source_file = StringTable::intern(tr.source_name, CHECK); | |
| + } | |
| + } | |
| + } | |
| + } | |
| } | |
| #if INCLUDE_JVMCI | |
| Index: src/hotspot/share/classfile/classFileParser.cpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp | |
| --- a/src/hotspot/share/classfile/classFileParser.cpp (revision cb059a6b1b1686b7adcb2e88536060f4f7d47118) | |
| +++ b/src/hotspot/share/classfile/classFileParser.cpp (date 1772628663042) | |
| @@ -2861,8 +2861,8 @@ | |
| const u1* const sde_buffer = cfs->current(); | |
| assert(sde_buffer != nullptr, "null sde buffer"); | |
| - // Don't bother storing it if there is no way to retrieve it | |
| - if (JvmtiExport::can_get_source_debug_extension()) { | |
| + // Store it if JVMTI needs it or if SMAP stack trace translation is enabled | |
| + if (JvmtiExport::can_get_source_debug_extension() || TranslateSourceDebugExtension) { | |
| assert((length+1) > length, "Overflow checking"); | |
| u1* const sde = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, u1, length+1); | |
| for (int i = 0; i < length; i++) { | |
| Index: src/hotspot/share/runtime/globals.hpp | |
| IDEA additional info: | |
| Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
| <+>UTF-8 | |
| =================================================================== | |
| diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp | |
| --- a/src/hotspot/share/runtime/globals.hpp (revision cb059a6b1b1686b7adcb2e88536060f4f7d47118) | |
| +++ b/src/hotspot/share/runtime/globals.hpp (date 1772628652495) | |
| @@ -180,6 +180,10 @@ | |
| "Print information about Java monitor locks when the stacks are " \ | |
| "dumped") \ | |
| \ | |
| + product(bool, TranslateSourceDebugExtension, true, \ | |
| + "Translate line numbers and file names in stack traces using " \ | |
| + "JSR-45 SourceDebugExtension (SMAP) data") \ | |
| + \ | |
| product_pd(bool, UseLargePages, \ | |
| "Use large page memory") \ | |
| \ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment