8242181: [Linux] Show source information when printing native stack traces in hs_err files
Reviewed-by: erikj, tschatzl, stuefe, ihse
This commit is contained in:
parent
fe0544f8a7
commit
13c0369646
@ -785,6 +785,8 @@ define SetupRunJtregTestBody
|
||||
# symbol lookup in hserr files
|
||||
ifeq ($$(call isTargetOs, windows), true)
|
||||
$1_JTREG_BASIC_OPTIONS += -e:_NT_SYMBOL_PATH
|
||||
else ifeq ($$(call isTargetOs, linux), true)
|
||||
$1_JTREG_BASIC_OPTIONS += -e:_JVM_DWARF_PATH=$$(SYMBOLS_IMAGE_DIR)
|
||||
endif
|
||||
|
||||
$1_JTREG_BASIC_OPTIONS += \
|
||||
|
@ -113,7 +113,7 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS],
|
||||
)
|
||||
fi
|
||||
|
||||
CFLAGS_DEBUG_SYMBOLS="-g"
|
||||
CFLAGS_DEBUG_SYMBOLS="-g -gdwarf-4"
|
||||
ASFLAGS_DEBUG_SYMBOLS="-g"
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then
|
||||
|
@ -943,7 +943,7 @@ var getJibProfilesProfiles = function (input, common, data) {
|
||||
target_cpu: input.build_cpu,
|
||||
dependencies: [
|
||||
"jtreg", "gnumake", "boot_jdk", "devkit", "jib", "jcov", testedProfileJdk,
|
||||
testedProfileTest
|
||||
testedProfileTest, testedProfile + ".jdk_symbols",
|
||||
],
|
||||
src: "src.conf",
|
||||
make_args: testOnlyMake,
|
||||
@ -951,7 +951,8 @@ var getJibProfilesProfiles = function (input, common, data) {
|
||||
"BOOT_JDK": common.boot_jdk_home,
|
||||
"JT_HOME": input.get("jtreg", "home_path"),
|
||||
"JDK_IMAGE_DIR": input.get(testedProfileJdk, "home_path"),
|
||||
"TEST_IMAGE_DIR": input.get(testedProfileTest, "home_path")
|
||||
"TEST_IMAGE_DIR": input.get(testedProfileTest, "home_path"),
|
||||
"SYMBOLS_IMAGE_DIR": input.get(testedProfile + ".jdk_symbols", "home_path")
|
||||
},
|
||||
labels: "test"
|
||||
}
|
||||
@ -991,17 +992,6 @@ var getJibProfilesProfiles = function (input, common, data) {
|
||||
profiles["run-test"] = concatObjects(profiles["run-test"], macosxRunTestExtra);
|
||||
profiles["run-test-prebuilt"] = concatObjects(profiles["run-test-prebuilt"], macosxRunTestExtra);
|
||||
}
|
||||
// On windows we want the debug symbols available at test time
|
||||
if (input.build_os == "windows") {
|
||||
windowsRunTestPrebuiltExtra = {
|
||||
dependencies: [ testedProfile + ".jdk_symbols" ],
|
||||
environment: {
|
||||
"SYMBOLS_IMAGE_DIR": input.get(testedProfile + ".jdk_symbols", "home_path"),
|
||||
}
|
||||
};
|
||||
profiles["run-test-prebuilt"] = concatObjects(profiles["run-test-prebuilt"],
|
||||
windowsRunTestPrebuiltExtra);
|
||||
}
|
||||
|
||||
// The profile run-test-prebuilt defines src.conf as the src bundle. When
|
||||
// running in Mach 5, this reduces the time it takes to populate the
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@ -35,7 +35,7 @@ bool Decoder::decode(address addr, char* buf, int buflen, int* offset, const voi
|
||||
return SymbolEngine::decode(addr, buf, buflen, offset, true);
|
||||
}
|
||||
|
||||
bool Decoder::get_source_info(address pc, char* buf, size_t buflen, int* line) {
|
||||
bool Decoder::get_source_info(address pc, char* buf, size_t buflen, int* line, bool is_pc_after_call) {
|
||||
return SymbolEngine::get_source_info(pc, buf, buflen, line);
|
||||
}
|
||||
|
||||
|
@ -511,6 +511,10 @@ const int ObjectAlignmentInBytes = 8;
|
||||
"error log in case of a crash.") \
|
||||
range(0, (uint64_t)max_jlong/1000) \
|
||||
\
|
||||
develop(intx, TraceDwarfLevel, 0, \
|
||||
"Debug levels for the dwarf parser") \
|
||||
range(0, 4) \
|
||||
\
|
||||
product(bool, SuppressFatalErrorMessage, false, \
|
||||
"Report NO fatal error message (avoid deadlock)") \
|
||||
\
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2020 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -113,8 +113,13 @@ bool Decoder::demangle(const char* symbol, char* buf, int buflen) {
|
||||
void Decoder::print_state_on(outputStream* st) {
|
||||
}
|
||||
|
||||
bool Decoder::get_source_info(address pc, char* buf, size_t buflen, int* line) {
|
||||
return false;
|
||||
bool Decoder::get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
|
||||
if (VMError::is_error_reported_in_current_thread()) {
|
||||
return get_error_handler_instance()->get_source_info(pc, filename, filename_len, line, is_pc_after_call);
|
||||
} else {
|
||||
MutexLocker locker(shared_decoder_lock(), Mutex::_no_safepoint_check_flag);
|
||||
return get_shared_instance()->get_source_info(pc, filename, filename_len, line, is_pc_after_call);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !_WINDOWS
|
||||
|
@ -63,6 +63,11 @@ public:
|
||||
// demangle a C++ symbol
|
||||
virtual bool demangle(const char* symbol, char* buf, int buflen) = 0;
|
||||
|
||||
// Get filename and line number information.
|
||||
virtual bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual decoder_status status() const {
|
||||
return _decoder_status;
|
||||
}
|
||||
@ -109,9 +114,11 @@ public:
|
||||
static bool demangle(const char* symbol, char* buf, int buflen);
|
||||
|
||||
// Attempts to retrieve source file name and line number associated with a pc.
|
||||
// If buf != NULL, points to a buffer of size buflen which will receive the
|
||||
// If filename != NULL, points to a buffer of size filename_len which will receive the
|
||||
// file name. File name will be silently truncated if output buffer is too small.
|
||||
static bool get_source_info(address pc, char* buf, size_t buflen, int* line);
|
||||
// If is_pc_after_call is true, then pc is treated as pointing to the next instruction
|
||||
// after a call. The source information for the call instruction is fetched in that case.
|
||||
static bool get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call = false);
|
||||
|
||||
static void print_state_on(outputStream* st);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@ -26,7 +26,8 @@
|
||||
|
||||
#if !defined(_WINDOWS) && !defined(__APPLE__)
|
||||
#include "decoder_elf.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
|
||||
ElfDecoder::~ElfDecoder() {
|
||||
if (_opened_elf_files != NULL) {
|
||||
@ -53,6 +54,45 @@ bool ElfDecoder::decode(address addr, char *buf, int buflen, int* offset, const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ElfDecoder::get_source_info(address pc, char* filename, size_t filename_len, int* line, bool is_pc_after_call) {
|
||||
assert(filename != nullptr && filename_len > 0 && line != nullptr, "Argument error");
|
||||
filename[0] = '\0';
|
||||
*line = -1;
|
||||
|
||||
char filepath[JVM_MAXPATHLEN];
|
||||
filepath[JVM_MAXPATHLEN - 1] = '\0';
|
||||
int offset_in_library = -1;
|
||||
if (!os::dll_address_to_library_name(pc, filepath, sizeof(filepath), &offset_in_library)) {
|
||||
// Method not found. offset_in_library should not overflow.
|
||||
DWARF_LOG_ERROR("Did not find library for address " INTPTR_FORMAT, p2i(pc))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filepath[JVM_MAXPATHLEN - 1] != '\0') {
|
||||
DWARF_LOG_ERROR("File path is too large to fit into buffer of size %d", JVM_MAXPATHLEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t unsigned_offset_in_library = (uint32_t)offset_in_library;
|
||||
|
||||
ElfFile* file = get_elf_file(filepath);
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
DWARF_LOG_INFO("##### Find filename and line number for offset " PTR32_FORMAT " in library %s #####",
|
||||
unsigned_offset_in_library, filepath);
|
||||
|
||||
if (!file->get_source_info(unsigned_offset_in_library, filename, filename_len, line, is_pc_after_call)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWARF_LOG_SUMMARY("pc: " INTPTR_FORMAT ", offset: " PTR32_FORMAT ", filename: %s, line: %u",
|
||||
p2i(pc), offset_in_library, filename, *line);
|
||||
DWARF_LOG_INFO("") // To structure the debug output better.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ElfFile* ElfDecoder::get_elf_file(const char* filepath) {
|
||||
ElfFile* file;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@ -45,6 +45,7 @@ public:
|
||||
ShouldNotReachHere();
|
||||
return false;
|
||||
}
|
||||
bool get_source_info(address pc, char* buf, size_t buflen, int* line, bool is_pc_after_call);
|
||||
|
||||
private:
|
||||
ElfFile* get_elf_file(const char* filepath);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@ -67,13 +67,36 @@ typedef Elf32_Sym Elf_Sym;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "jvm_md.h"
|
||||
#include "globalDefinitions.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/decoder.hpp"
|
||||
|
||||
#ifdef ASSERT
|
||||
// Helper macros to print different log levels during DWARF parsing
|
||||
#define DWARF_LOG_SUMMARY(format, ...) DWARF_LOG_WITH_LEVEL(1, format, ##__VA_ARGS__) // Same level as error logging
|
||||
#define DWARF_LOG_ERROR(format, ...) DWARF_LOG_WITH_LEVEL(1, format, ##__VA_ARGS__)
|
||||
#define DWARF_LOG_INFO(format, ...) DWARF_LOG_WITH_LEVEL(2, format, ##__VA_ARGS__)
|
||||
#define DWARF_LOG_DEBUG(format, ...) DWARF_LOG_WITH_LEVEL(3, format, ##__VA_ARGS__)
|
||||
#define DWARF_LOG_TRACE(format, ...) DWARF_LOG_WITH_LEVEL(4, format, ##__VA_ARGS__)
|
||||
|
||||
#define DWARF_LOG_WITH_LEVEL(level, format, ...) \
|
||||
if (TraceDwarfLevel >= level) { \
|
||||
tty->print("[dwarf] "); \
|
||||
tty->print_cr(format, ##__VA_ARGS__); \
|
||||
}
|
||||
#else
|
||||
#define DWARF_LOG_SUMMARY(format, ...)
|
||||
#define DWARF_LOG_ERROR(format, ...)
|
||||
#define DWARF_LOG_INFO(format, ...)
|
||||
#define DWARF_LOG_DEBUG(format, ...)
|
||||
#define DWARF_LOG_TRACE(format, ...)
|
||||
#endif
|
||||
|
||||
class ElfStringTable;
|
||||
class ElfSymbolTable;
|
||||
class ElfFuncDescTable;
|
||||
class DwarfFile;
|
||||
|
||||
// ELF section, may or may not have cached data
|
||||
class ElfSection {
|
||||
@ -96,21 +119,21 @@ private:
|
||||
};
|
||||
|
||||
class FileReader : public StackObj {
|
||||
protected:
|
||||
protected:
|
||||
FILE* const _fd;
|
||||
public:
|
||||
public:
|
||||
FileReader(FILE* const fd) : _fd(fd) {};
|
||||
bool read(void* buf, size_t size);
|
||||
int read_buffer(void* buf, size_t size);
|
||||
bool set_position(long offset);
|
||||
size_t read_buffer(void* buf, size_t size);
|
||||
virtual bool set_position(long offset);
|
||||
};
|
||||
|
||||
// Mark current position, so we can get back to it after
|
||||
// reads.
|
||||
class MarkedFileReader : public FileReader {
|
||||
private:
|
||||
protected:
|
||||
long _marked_pos;
|
||||
public:
|
||||
public:
|
||||
MarkedFileReader(FILE* const fd);
|
||||
~MarkedFileReader();
|
||||
|
||||
@ -125,7 +148,7 @@ public:
|
||||
class ElfFile: public CHeapObj<mtInternal> {
|
||||
friend class ElfDecoder;
|
||||
|
||||
private:
|
||||
private:
|
||||
// link ElfFiles
|
||||
ElfFile* _next;
|
||||
|
||||
@ -133,9 +156,6 @@ private:
|
||||
char* _filepath;
|
||||
FILE* _file;
|
||||
|
||||
// Elf header
|
||||
Elf_Ehdr _elfHdr;
|
||||
|
||||
// symbol tables
|
||||
ElfSymbolTable* _symbol_tables;
|
||||
|
||||
@ -150,16 +170,18 @@ private:
|
||||
|
||||
NullDecoder::decoder_status _status;
|
||||
|
||||
public:
|
||||
DwarfFile* _dwarf_file;
|
||||
static const char* USR_LIB_DEBUG_DIRECTORY;
|
||||
protected:
|
||||
// Elf header
|
||||
Elf_Ehdr _elfHdr;
|
||||
|
||||
public:
|
||||
ElfFile(const char* filepath);
|
||||
~ElfFile();
|
||||
virtual ~ElfFile();
|
||||
|
||||
bool decode(address addr, char* buf, int buflen, int* offset);
|
||||
|
||||
const char* filepath() const {
|
||||
return _filepath;
|
||||
}
|
||||
|
||||
bool same_elf_file(const char* filepath) const {
|
||||
assert(filepath != NULL, "null file path");
|
||||
return (_filepath != NULL && !strcmp(filepath, _filepath));
|
||||
@ -175,7 +197,10 @@ public:
|
||||
// is not set at all, or if the file can not be read.
|
||||
// On systems other than linux it always returns false.
|
||||
static bool specifies_noexecstack(const char* filepath) NOT_LINUX({ return false; });
|
||||
private:
|
||||
|
||||
bool get_source_info(uint32_t offset_in_library, char* filename, size_t filename_len, int* line, bool is_pc_after_call);
|
||||
|
||||
private:
|
||||
// sanity check, if the file is a real elf file
|
||||
static bool is_elf_file(Elf_Ehdr&);
|
||||
|
||||
@ -188,9 +213,11 @@ private:
|
||||
ElfFile* next() const { return _next; }
|
||||
void set_next(ElfFile* file) { _next = file; }
|
||||
|
||||
#if defined(PPC64) && !defined(ABI_ELFv2)
|
||||
// find a section by name, return section index
|
||||
// if there is no such section, return -1
|
||||
int section_by_name(const char* name, Elf_Shdr& hdr);
|
||||
#endif
|
||||
|
||||
// string tables are stored in a linked list
|
||||
void add_string_table(ElfStringTable* table);
|
||||
@ -201,17 +228,664 @@ private:
|
||||
// return a string table at specified section index
|
||||
ElfStringTable* get_string_table(int index);
|
||||
|
||||
|
||||
FILE* const fd() const { return _file; }
|
||||
|
||||
// Cleanup string, symbol and function descriptor tables
|
||||
void cleanup_tables();
|
||||
|
||||
public:
|
||||
bool create_new_dwarf_file(const char* filepath);
|
||||
|
||||
// Struct to store the debug info read from the .gnu_debuglink section.
|
||||
struct DebugInfo {
|
||||
static const uint8_t CRC_LEN = 4;
|
||||
|
||||
char _dwarf_filename[JVM_MAXPATHLEN];
|
||||
uint32_t _crc;
|
||||
};
|
||||
|
||||
// Helper class to create DWARF paths when loading a DWARF file.
|
||||
class DwarfFilePath {
|
||||
private:
|
||||
static const uint16_t MAX_DWARF_PATH_LENGTH = JVM_MAXPATHLEN;
|
||||
const char* _filename;
|
||||
char _path[MAX_DWARF_PATH_LENGTH];
|
||||
const uint32_t _crc;
|
||||
uint16_t _null_terminator_index; // Index for the current null terminator of the string stored in _path
|
||||
|
||||
bool check_valid_path() const {
|
||||
return _path[MAX_DWARF_PATH_LENGTH - 1] == '\0';
|
||||
}
|
||||
|
||||
void update_null_terminator_index() {
|
||||
_null_terminator_index = strlen(_path);
|
||||
}
|
||||
|
||||
bool copy_to_path_index(uint16_t index_in_path, const char* src);
|
||||
|
||||
public:
|
||||
DwarfFilePath(DebugInfo& debug_info)
|
||||
: _filename(debug_info._dwarf_filename), _crc(debug_info._crc), _null_terminator_index(0) {
|
||||
_path[MAX_DWARF_PATH_LENGTH - 1] = '\0'; // Ensures to have a null terminated string and not read beyond the buffer limit.
|
||||
}
|
||||
|
||||
const char* path() const {
|
||||
return _path;
|
||||
}
|
||||
|
||||
const char* filename() const {
|
||||
return _filename;
|
||||
}
|
||||
|
||||
uint32_t crc() const {
|
||||
return _crc;
|
||||
}
|
||||
|
||||
bool set(const char* src);
|
||||
|
||||
bool set_filename_after_last_slash() {
|
||||
return set_after_last_slash(_filename);
|
||||
}
|
||||
|
||||
bool set_after_last_slash(const char* src);
|
||||
bool append(const char* src);
|
||||
};
|
||||
|
||||
// Load the DWARF file (.debuginfo) that belongs to this file either from (checked in listed order):
|
||||
// - Same directory as the library file.
|
||||
// - User defined path in environmental variable _JVM_DWARF_PATH.
|
||||
// - Subdirectory .debug in same directory as the library file.
|
||||
// - /usr/lib/debug directory
|
||||
bool load_dwarf_file();
|
||||
|
||||
|
||||
bool read_debug_info(DebugInfo* debug_info) const;
|
||||
|
||||
bool load_dwarf_file_from_same_directory(DwarfFilePath& dwarf_file_path);
|
||||
bool load_dwarf_file_from_env_var_path(DwarfFilePath& dwarf_file_path);
|
||||
bool load_dwarf_file_from_env_path_folder(DwarfFilePath& dwarf_file_path, const char* dwarf_path_from_env, const char* folder);
|
||||
bool load_dwarf_file_from_debug_sub_directory(DwarfFilePath& dwarf_file_path);
|
||||
bool load_dwarf_file_from_usr_lib_debug(DwarfFilePath& dwarf_file_path);
|
||||
bool open_valid_debuginfo_file(const DwarfFilePath& dwarf_file_path);
|
||||
static uint32_t get_file_crc(FILE* const file);
|
||||
static uint gnu_debuglink_crc32(uint32_t crc, uint8_t* buf, size_t len);
|
||||
|
||||
protected:
|
||||
FILE* const fd() const { return _file; }
|
||||
|
||||
// Read the section header of section 'name'.
|
||||
bool read_section_header(const char* name, Elf_Shdr& hdr) const;
|
||||
bool is_valid_dwarf_file() const;
|
||||
|
||||
public:
|
||||
// For whitebox test
|
||||
static bool _do_not_cache_elf_section;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This class parses and reads filename and line number information from an associated .debuginfo file that belongs to
|
||||
* this ELF file or directly from this ELF file if there is no separate .debuginfo file. The debug info is written by GCC
|
||||
* in DWARF - a standardized debugging data format. There are special sections where the DWARF info is written to. These
|
||||
* sections can either be put into the same ELF file or a separate .debuginfo file. For simplicity, when referring to the
|
||||
* "DWARF file" or the ".debuginfo file" we just mean the file that contains the required DWARF sections. The current version
|
||||
* of GCC uses DWARF version 4 as default which is defined in the official standard: http://www.dwarfstd.org/doc/DWARF4.pdf.
|
||||
* This class is able to parse 32-bit DWARF version 4 for 32 and 64-bit Linux builds. GCC does not emit 64-bit DWARF and
|
||||
* therefore is not supported by this parser. For some reason, GCC emits DWARF version 3 for the .debug_line section as a
|
||||
* default. This parser was therefore adapted to support DWARF version 3 and 4 for the .debug_line section. Apart from that,
|
||||
* other DWARF versions, especially the newest version 5, are not (yet) supported.
|
||||
*
|
||||
* Description of used DWARF file sections:
|
||||
* - .debug_aranges: A table that consists of sets of variable length entries, each set describing the portion of the
|
||||
* program's address space that is covered by a single compilation unit. In other words, the entries
|
||||
* describe a mapping between addresses and compilation units.
|
||||
* - .debug_info: The core DWARF data containing DWARF Information Entries (DIEs). Each DIE consists of a tag and a
|
||||
* series of attributes. Each (normal) compilation unit is represented by a DIE with the tag
|
||||
* DW_TAG_compile_unit and contains children. For our purposes, we are only interested in this DIE to
|
||||
* get to the .debug_line section. We do not care about the children. This parser currently only
|
||||
* supports normal compilation units and no partial compilation or type units.
|
||||
* - .debug_abbrev: Represents abbreviation tables for all compilation units. A table for a specific compilation unit
|
||||
* consists of a series of abbreviation declarations. Each declaration specifies a tag and attributes
|
||||
* for a DIE. The DIEs from the compilation units in the .debug_info section need the abbreviation table
|
||||
* to decode their attributes (their meaning and size).
|
||||
* - .debug_line: Contains filename and line number information for each compilation unit. To get the information, a
|
||||
* state machine needs to be executed which generates a matrix. Each row of this matrix describes the
|
||||
* filename and line number (among other information) for a specific offset in the associated ELF library
|
||||
* file. The state machine is executed until the row for the requested offset is found. The filename and
|
||||
* line number information can then be fetched with the current register values of the state machine.
|
||||
*
|
||||
* Algorithm
|
||||
* ---------
|
||||
* Given: Offset into the ELF file library.
|
||||
* Return: Filename and line number for this offset.
|
||||
* (1) First, the path to the .debuginfo DWARF file is found by inspecting the .gnu_debuglink section of the library file.
|
||||
* The DWARF file is then opened by calling the constructor of this class. Once this is done, the processing of the
|
||||
* DWARF file is initiated by calling find_filename_and_line_number().
|
||||
* (2) Find the compilation unit offset by reading entries from the section .debug_aranges, which contain address range
|
||||
* descriptors, until we find the correct descriptor that includes the library offset.
|
||||
* (3) Find the .debug_line offset for the line number information program from the .debug_info section:
|
||||
* (a) Parse the compilation unit header from the .debug_info section at the offset obtained by (2).
|
||||
* (b) Read the debug_abbrev_offset into the .debug_abbrev section that belongs to this compilation unit from the
|
||||
* header obtained in (3a).
|
||||
* (c) Read the abbreviation code that immediately follows the compilation unit header from (3a) which is needed to
|
||||
* find the correct entry in the .debug_abbrev section.
|
||||
* (d) Find the correct entry in the abbreviation table in the .debug_abbrev section by starting to parse entries at
|
||||
* the debug_abbrev_offset from (3b) until we find the correct one matching the abbreviation code from (3c).
|
||||
* (e) Read the specified attributes of the abbreviation entry from (3d) from the compilation unit (in the .debug_info
|
||||
* section) until we find the attribute DW_AT_stmt_list. This attributes represents an offset into the .debug_line
|
||||
* section which contains the line number program information to get the filename and the line number.
|
||||
* (4) Find the filename and line number belonging to the given library offset by running the line number program state
|
||||
* machine with its registers. This creates a matrix where each row stores information for specific addresses (library
|
||||
* offsets). The state machine executes different opcodes which modify the state machine registers. Certain opcodes
|
||||
* will add a new row to the matrix by taking the current values of state machine registers. As soon as the correct
|
||||
* matrix row matching the library offset is found, we can read the line number from the line register of the state
|
||||
* machine and parse the filename from the line number program header with the given file index from the file register
|
||||
* of the state machine.
|
||||
*
|
||||
* More details about the different phases can be found at the associated classes and methods. A visualization of the
|
||||
* algorithm inside the different sections can be found in the class comments for DebugAranges, DebugAbbrev and
|
||||
* LineNumberProgram further down in this file.
|
||||
*
|
||||
* Available (develop) log levels (-XX:TraceDwarfLevel=[1,4]) which are only present in debug builds. Each level prints
|
||||
* all the logs of the previous levels and adds some more fine-grained logging:
|
||||
* - Level 1 (summary + errors):
|
||||
* - Prints the path of parsed DWARF file together with the resulting source information.
|
||||
* - Prints all errors.
|
||||
* - Level 2 (info):
|
||||
* - Prints the found offsets of all DWARF sections
|
||||
* - Level 3 (debug):
|
||||
* - Prints the results of the steps (1) - (4) together with the generated line information matrix.
|
||||
* - Level 4 (trace):
|
||||
* - Complete information about intermediate states/results when parsing the DWARF file.
|
||||
*/
|
||||
class DwarfFile : public ElfFile {
|
||||
|
||||
static constexpr uint8_t ADDRESS_SIZE = NOT_LP64(4) LP64_ONLY(8);
|
||||
// We only support 32-bit DWARF (emitted by GCC) which uses 32-bit values for DWARF section lengths and offsets
|
||||
// relative to the beginning of a section.
|
||||
static constexpr uint8_t DWARF_SECTION_OFFSET_SIZE = 4;
|
||||
|
||||
class MarkedDwarfFileReader : public MarkedFileReader {
|
||||
private:
|
||||
long _current_pos;
|
||||
long _max_pos; // Used to guarantee that we stop reading in case we reached the end of a section.
|
||||
|
||||
bool read_leb128(uint64_t* result, int8_t check_size, bool is_signed);
|
||||
public:
|
||||
MarkedDwarfFileReader(FILE* const fd) : MarkedFileReader(fd), _current_pos(-1), _max_pos(-1) {}
|
||||
|
||||
virtual bool set_position(long new_pos);
|
||||
long get_position() const { return _current_pos; }
|
||||
void set_max_pos(long max_pos) { _max_pos = max_pos; }
|
||||
// Have we reached the limit of maximally allowable bytes to read? Used to ensure to stop reading when a section ends.
|
||||
bool has_bytes_left() const;
|
||||
// Call this if another file reader has changed the position of the same file handle.
|
||||
bool update_to_stored_position();
|
||||
// Must be called to restore the old position before this file reader changed it with update_to_stored_position().
|
||||
bool reset_to_previous_position();
|
||||
bool move_position(long offset);
|
||||
bool read_sbyte(int8_t* result);
|
||||
bool read_byte(uint8_t* result);
|
||||
bool read_word(uint16_t* result);
|
||||
bool read_dword(uint32_t* result);
|
||||
bool read_qword(uint64_t* result);
|
||||
bool read_uleb128_ignore(int8_t check_size = -1);
|
||||
bool read_uleb128(uint64_t* result, int8_t check_size = -1);
|
||||
bool read_sleb128(int64_t* result, int8_t check_size = -1);
|
||||
// Reads 4 bytes for 32-bit and 8 bytes for 64-bit builds.
|
||||
bool read_address_sized(uintptr_t* result);
|
||||
bool read_string(char* result = nullptr, size_t result_len = 0);
|
||||
};
|
||||
|
||||
// (2) Processing the .debug_aranges section to find the compilation unit which covers offset_in_library.
|
||||
// This is specified in section 6.1.2 of the DWARF 4 spec.
|
||||
//
|
||||
// Structure of .debug_aranges:
|
||||
// Section Header
|
||||
// % Table of variable length sets describing the address space covered by a compilation unit
|
||||
// % Set 1
|
||||
// ...
|
||||
// % Set i:
|
||||
// % Set header
|
||||
// ...
|
||||
// debug_info_offset -> offset to compilation unit
|
||||
// % Series of address range descriptors [beginning_address, range_length]:
|
||||
// % Descriptor 1
|
||||
// ...
|
||||
// % Descriptor j:
|
||||
// beginning_address <= offset_in_library < beginning_address + range_length?
|
||||
// => Found the correct set covering offset_in_library. Take debug_info_offset from the set header to get
|
||||
// to the correct compilation unit in .debug_info.
|
||||
class DebugAranges {
|
||||
|
||||
// The header is defined in section 6.1.2 of the DWARF 4 spec.
|
||||
struct DebugArangesSetHeader {
|
||||
// The total length of all of the entries for that set, not including the length field itself.
|
||||
uint32_t _unit_length;
|
||||
|
||||
// This number is specific to the address lookup table and is independent of the DWARF version number.
|
||||
uint16_t _version;
|
||||
|
||||
// The offset from the beginning of the .debug_info or .debug_types section of the compilation unit header referenced
|
||||
// by the set. In this parser we only use it as offset into .debug_info. This must be 4 bytes for 32-bit DWARF.
|
||||
uint32_t _debug_info_offset;
|
||||
|
||||
// The size of an address in bytes on the target architecture, 4 bytes for 32-bit and 8 bytes for 64-bit Linux builds.
|
||||
uint8_t _address_size;
|
||||
|
||||
// The size of a segment selector in bytes on the target architecture. This should be 0.
|
||||
uint8_t _segment_size;
|
||||
};
|
||||
|
||||
// Address descriptor defining a range that is covered by a compilation unit. It is defined in section 6.1.2 after
|
||||
// the set header in the DWARF 4 spec.
|
||||
struct AddressDescriptor {
|
||||
uintptr_t beginning_address = 0;
|
||||
uintptr_t range_length = 0;
|
||||
};
|
||||
|
||||
DwarfFile* _dwarf_file;
|
||||
MarkedDwarfFileReader _reader;
|
||||
uint32_t _section_start_address;
|
||||
|
||||
bool read_section_header();
|
||||
bool read_set_header(DebugArangesSetHeader& header);
|
||||
bool read_address_descriptors(const DwarfFile::DebugAranges::DebugArangesSetHeader& header,
|
||||
uint32_t offset_in_library, bool& found_matching_set);
|
||||
bool read_address_descriptor(AddressDescriptor& descriptor);
|
||||
static bool does_match_offset(uint32_t offset_in_library, const AddressDescriptor& descriptor) ;
|
||||
static bool is_terminating_entry(const AddressDescriptor& descriptor);
|
||||
public:
|
||||
DebugAranges(DwarfFile* dwarf_file) : _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _section_start_address(0) {}
|
||||
bool find_compilation_unit_offset(uint32_t offset_in_library, uint32_t* compilation_unit_offset);
|
||||
|
||||
};
|
||||
|
||||
// (3a-c,e) The compilation unit is read from the .debug_info section. The structure of .debug_info is shown in the
|
||||
// comments of class DebugAbbrev.
|
||||
class CompilationUnit {
|
||||
|
||||
// Attribute form encodings from Figure 21 in section 7.5 of the DWARF 4 spec.
|
||||
static constexpr uint8_t DW_FORM_addr = 0x01; // address
|
||||
static constexpr uint8_t DW_FORM_block2 = 0x03; // block
|
||||
static constexpr uint8_t DW_FORM_block4 = 0x04; // block
|
||||
static constexpr uint8_t DW_FORM_data2 = 0x05; // constant
|
||||
static constexpr uint8_t DW_FORM_data4 = 0x06; // constant
|
||||
static constexpr uint8_t DW_FORM_data8 = 0x07; // constant
|
||||
static constexpr uint8_t DW_FORM_string = 0x08; // string
|
||||
static constexpr uint8_t DW_FORM_block = 0x09; // block
|
||||
static constexpr uint8_t DW_FORM_block1 = 0x0a; // block
|
||||
static constexpr uint8_t DW_FORM_data1 = 0x0b; // constant
|
||||
static constexpr uint8_t DW_FORM_flag = 0x0c; // flag
|
||||
static constexpr uint8_t DW_FORM_sdata = 0x0d; // constant
|
||||
static constexpr uint8_t DW_FORM_strp = 0x0e; // string
|
||||
static constexpr uint8_t DW_FORM_udata = 0x0f; // constant
|
||||
static constexpr uint8_t DW_FORM_ref_addr = 0x10; // reference0;
|
||||
static constexpr uint8_t DW_FORM_ref1 = 0x11; // reference
|
||||
static constexpr uint8_t DW_FORM_ref2 = 0x12; // reference
|
||||
static constexpr uint8_t DW_FORM_ref4 = 0x13; // reference
|
||||
static constexpr uint8_t DW_FORM_ref8 = 0x14; // reference
|
||||
static constexpr uint8_t DW_FORM_ref_udata = 0x15; // reference
|
||||
static constexpr uint8_t DW_FORM_indirect = 0x16; // see Section 7.5.3
|
||||
static constexpr uint8_t DW_FORM_sec_offset = 0x17; // lineptr, loclistptr, macptr, rangelistptr
|
||||
static constexpr uint8_t DW_FORM_exprloc = 0x18;// exprloc
|
||||
static constexpr uint8_t DW_FORM_flag_present = 0x19; // flag
|
||||
static constexpr uint8_t DW_FORM_ref_sig8 = 0x20; // reference
|
||||
|
||||
// The header is defined in section 7.5.1.1 of the DWARF 4 spec.
|
||||
struct CompilationUnitHeader {
|
||||
// The length of the .debug_info contribution for that compilation unit, not including the length field itself.
|
||||
uint32_t _unit_length;
|
||||
|
||||
// The version of the DWARF information for the compilation unit. The value in this field is 4 for DWARF 4.
|
||||
uint16_t _version;
|
||||
|
||||
// The offset into the .debug_abbrev section. This offset associates the compilation unit with a particular set of
|
||||
// debugging information entry abbreviations.
|
||||
uint32_t _debug_abbrev_offset;
|
||||
|
||||
// The size in bytes of an address on the target architecture, 4 bytes for 32-bit and 8 bytes for 64-bit Linux builds.
|
||||
uint8_t _address_size;
|
||||
};
|
||||
|
||||
DwarfFile* _dwarf_file;
|
||||
MarkedDwarfFileReader _reader;
|
||||
CompilationUnitHeader _header;
|
||||
const uint32_t _compilation_unit_offset;
|
||||
|
||||
// Result of a request initiated by find_debug_line_offset().
|
||||
uint32_t _debug_line_offset;
|
||||
|
||||
bool read_header();
|
||||
public:
|
||||
CompilationUnit(DwarfFile* dwarf_file, uint32_t compilation_unit_offset)
|
||||
: _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _compilation_unit_offset(compilation_unit_offset), _debug_line_offset(0) {}
|
||||
|
||||
bool find_debug_line_offset(uint32_t* debug_line_offset);
|
||||
bool read_attribute_value(uint64_t attribute_form, bool is_DW_AT_stmt_list_attribute);
|
||||
};
|
||||
|
||||
// (3d) Read from the .debug_abbrev section at the debug_abbrev_offset specified by the compilation unit header.
|
||||
//
|
||||
// The interplay between the .debug_info and .debug_abbrev sections is more complex. The following visualization of the structure
|
||||
// of both sections support the comments found in the parsing steps of the CompilationUnit and DebugAbbrev class.
|
||||
//
|
||||
// Structure of .debug_abbrev:
|
||||
// Section Header
|
||||
// % Series of abbreviation tables
|
||||
// % Abbreviation table 1
|
||||
// ...
|
||||
// % Abbreviation table for compilation unit at debug_abbrev_offset:
|
||||
// % Series of declarations:
|
||||
// % Declaration 1:
|
||||
// abbreviation code
|
||||
// tag
|
||||
// DW_CHILDREN_yes/no
|
||||
// % Series of attribute specifications
|
||||
// % Attribute specification 1:
|
||||
// attribute name
|
||||
// attribute form
|
||||
// ...
|
||||
// % Last attribute specification:
|
||||
// 0
|
||||
// 0
|
||||
// ...
|
||||
// % Declaration i:
|
||||
// Abbrev code read from compilation unit [AC]
|
||||
// DW_TAG_compile_unit
|
||||
// DW_CHILDREN_yes
|
||||
// % Series of attribute specifications
|
||||
// % Attribute specification 1 [AS1]
|
||||
// ...
|
||||
// % Attribute specification j [ASj]:
|
||||
// DW_AT_stmt_list
|
||||
// DW_FORM_sec_offset
|
||||
//
|
||||
//
|
||||
// Structure of .debug_info:
|
||||
// Section Header
|
||||
// % Series of compilation units
|
||||
// % Compilation unit 1
|
||||
// ...
|
||||
// % Compilation unit i for library offset fetched from .debug_aranges:
|
||||
// % Compilation unit header:
|
||||
// ...
|
||||
// debug_abbrev_offset -> offset for abbreviation table in .debug_abbrev for this compilation unit
|
||||
// ...
|
||||
// Abbrev code -> used in .debug_abbrev to find the correct declaration [AC]
|
||||
// % Series of attribute values
|
||||
// Attribute value 1 (in the format defined by attribute specification 1 [AS1])
|
||||
// ...
|
||||
// Attribute value j (in the format defined by attribute specification j [ASj]):
|
||||
// => Specifies Offset to line number program for this compilation unit in .debug_line
|
||||
class DebugAbbrev {
|
||||
|
||||
struct AbbreviationDeclaration {
|
||||
uint64_t _abbrev_code;
|
||||
uint64_t _tag;
|
||||
uint8_t _has_children;
|
||||
};
|
||||
|
||||
struct AttributeSpecification {
|
||||
uint64_t _name;
|
||||
uint64_t _form;
|
||||
};
|
||||
|
||||
// Tag encoding from Figure 18 in section 7.5 of the DWARF 4 spec.
|
||||
static constexpr uint8_t DW_TAG_compile_unit = 0x11;
|
||||
|
||||
// Child determination encoding from Figure 19 in section 7.5 of the DWARF 4 spec.
|
||||
static constexpr uint8_t DW_CHILDREN_yes = 0x01;
|
||||
|
||||
// Attribute encoding from Figure 20 in section 7.5 of the DWARF 4 spec.
|
||||
static constexpr uint8_t DW_AT_stmt_list = 0x10;
|
||||
|
||||
/* There is no specific header for this section */
|
||||
|
||||
DwarfFile* _dwarf_file;
|
||||
MarkedDwarfFileReader _reader;
|
||||
CompilationUnit* _compilation_unit; // Need to read from compilation unit while parsing the entries in .debug_abbrev.
|
||||
|
||||
// Result field of a request
|
||||
uint32_t* _debug_line_offset;
|
||||
|
||||
bool read_declaration(AbbreviationDeclaration& declaration);
|
||||
static bool is_wrong_or_unsupported_format(const AbbreviationDeclaration& declaration);
|
||||
bool read_attribute_specifications(bool is_DW_TAG_compile_unit);
|
||||
bool read_attribute_specification(AttributeSpecification& specification);
|
||||
static bool is_terminating_specification(const AttributeSpecification& attribute_specification) ;
|
||||
|
||||
public:
|
||||
DebugAbbrev(DwarfFile* dwarf_file, CompilationUnit* compilation_unit) :
|
||||
_dwarf_file(dwarf_file), _reader(_dwarf_file->fd()), _compilation_unit(compilation_unit),
|
||||
_debug_line_offset(nullptr) {}
|
||||
|
||||
bool read_section_header(uint32_t debug_abbrev_offset);
|
||||
bool find_debug_line_offset(uint64_t abbrev_code);
|
||||
};
|
||||
|
||||
// (4) The line number program for the compilation unit at the offset of the .debug_line obtained by (3).
|
||||
// For some reason, earlier GCC versions emit the line number program in DWARF 2 or 3 format even though the
|
||||
// default is DWARF 4. It also mixes the standards (see comments in the parsing code).
|
||||
//
|
||||
// Therefore, this class supports DWARF 2, 3 and 4 parsing as specified in section 6.2 of the DWARF specs.
|
||||
// The parsing of DWARF 2 is already covered by the parsing of DWARF 3 as they use the shared opcodes in the same way.
|
||||
// The parsing of DWARF 4, however, needs some adaptation as it consumes more data for some shared opcodes.
|
||||
//
|
||||
// DWARF 2 standard: https://dwarfstd.org/doc/dwarf-2.0.0.pdf
|
||||
// DWARF 3 standard: https://dwarfstd.org/doc/Dwarf3.pdf
|
||||
//
|
||||
//
|
||||
// Structure of .debug_ling:
|
||||
// Section Header
|
||||
// % Series of line number program entries for each compilation unit
|
||||
// % Line number program 1
|
||||
// ...
|
||||
// % Line number program i for our compilation unit:
|
||||
// % Line program header unit header:
|
||||
// ...
|
||||
// version -> currently emits version 3 by default
|
||||
// ...
|
||||
// file_name -> sequence of file names
|
||||
// % Sequence of opcodes as part of the line number program to build the line number information matrix:
|
||||
// % Format of matrix: [offset, line, directory_index, file_index]
|
||||
// % Line 1
|
||||
// ...
|
||||
// % Line j:
|
||||
// [offset matching offset_in_library, line, directory_index, file_index]
|
||||
// => Get line number + look up file_index in file_name list (pick file_index'th string)
|
||||
class LineNumberProgram {
|
||||
|
||||
// Standard opcodes for the line number program defined in section 6.2.5.2 of the DWARF 4 spec.
|
||||
static constexpr uint8_t DW_LNS_copy = 1;
|
||||
static constexpr uint8_t DW_LNS_advance_pc = 2;
|
||||
static constexpr uint8_t DW_LNS_advance_line = 3;
|
||||
static constexpr uint8_t DW_LNS_set_file = 4;
|
||||
static constexpr uint8_t DW_LNS_set_column = 5;
|
||||
static constexpr uint8_t DW_LNS_negate_stmt = 6;
|
||||
static constexpr uint8_t DW_LNS_set_basic_block = 7;
|
||||
static constexpr uint8_t DW_LNS_const_add_pc = 8;
|
||||
static constexpr uint8_t DW_LNS_fixed_advance_pc = 9;
|
||||
static constexpr uint8_t DW_LNS_set_prologue_end = 10; // Introduced with DWARF 3
|
||||
static constexpr uint8_t DW_LNS_set_epilogue_begin = 11; // Introduced with DWARF 3
|
||||
static constexpr uint8_t DW_LNS_set_isa = 12; // Introduced with DWARF 3
|
||||
|
||||
// Extended opcodes for the line number program defined in section 6.2.5.2 of the DWARF 4 spec.
|
||||
static constexpr uint8_t DW_LNE_end_sequence = 1;
|
||||
static constexpr uint8_t DW_LNE_set_address = 2;
|
||||
static constexpr uint8_t DW_LNE_define_file = 3;
|
||||
static constexpr uint8_t DW_LNE_set_discriminator = 4; // Introduced with DWARF 4
|
||||
|
||||
// The header is defined in section 6.2.4 of the DWARF 4 spec.
|
||||
struct LineNumberProgramHeader {
|
||||
// The size in bytes of the line number information for this compilation unit, not including the unit_length
|
||||
// field itself. 32-bit DWARF uses 4 bytes.
|
||||
uint32_t _unit_length;
|
||||
|
||||
// The version of the DWARF information for the line number program unit. The value in this field should be 4 for
|
||||
// DWARF 4 and version 3 as used for DWARF 3.
|
||||
uint16_t _version;
|
||||
|
||||
// The number of bytes following the header_length field to the beginning of the first byte of the line number
|
||||
// program itself. 32-bit DWARF uses 4 bytes.
|
||||
uint32_t _header_length;
|
||||
|
||||
// The size in bytes of the smallest target machine instruction. Line number program opcodes that alter the address
|
||||
// and op_index registers use this and maximum_operations_per_instruction in their calculations.
|
||||
uint8_t _minimum_instruction_length;
|
||||
|
||||
// The maximum number of individual operations that may be encoded in an instruction. Line number program opcodes
|
||||
// that alter the address and op_index registers use this and minimum_instruction_length in their calculations.
|
||||
// For non-VLIW architectures, this field is 1, the op_index register is always 0, and the operation pointer is
|
||||
// simply the address register. This is only used with DWARF 4.
|
||||
uint8_t _maximum_operations_per_instruction;
|
||||
|
||||
// The initial value of the is_stmt register.
|
||||
uint8_t _default_is_stmt;
|
||||
|
||||
// This parameter affects the meaning of the special opcodes.
|
||||
int8_t _line_base;
|
||||
|
||||
// This parameter affects the meaning of the special opcodes.
|
||||
uint8_t _line_range;
|
||||
|
||||
// The number assigned to the first special opcode.
|
||||
uint8_t _opcode_base;
|
||||
|
||||
// This array specifies the number of LEB128 operands for each of the standard opcodes. The first element of the
|
||||
// array corresponds to the opcode whose value is 1, and the last element corresponds to the opcode whose value is
|
||||
// opcode_base-1. DWARF 2 uses 9 standard opcodes while DWARF 3 and 4 use 12.
|
||||
uint8_t _standard_opcode_lengths[12];
|
||||
|
||||
/*
|
||||
* The following fields are not part of the real header and are only used for the implementation.
|
||||
*/
|
||||
// Offset where the filename strings are starting in header.
|
||||
long _file_names_offset;
|
||||
|
||||
// _header_length only specifies the number of bytes following the _header_length field. It does not include
|
||||
// the size of _unit_length, _version and _header_length itself. This constant represents the number of missing
|
||||
// bytes to get the real size of the header:
|
||||
// sizeof(_unit_length) + sizeof(_version) + sizeof(_header_length) = 4 + 2 + 4 = 10
|
||||
static constexpr uint8_t HEADER_DESCRIPTION_BYTES = 10;
|
||||
};
|
||||
|
||||
// The line number program state consists of several registers that hold the current state of the line number program
|
||||
// state machine. The state/different state registers are defined in section 6.2.2 of the DWARF 4 spec. Most of these
|
||||
// fields (state registers) are not used to get the filename and the line number information.
|
||||
struct LineNumberProgramState : public CHeapObj<mtInternal> {
|
||||
// The program-counter value corresponding to a machine instruction generated by the compiler.
|
||||
// 4 bytes on 32-bit and 8 bytes on 64-bit.
|
||||
uintptr_t _address;
|
||||
|
||||
// The index of an operation within a VLIW instruction. The index of the first operation is 0. For non-VLIW
|
||||
// architectures, this register will always be 0.
|
||||
// The address and op_index registers, taken together, form an operation pointer that can reference any
|
||||
// individual operation with the instruction stream. This field was introduced with DWARF 4.
|
||||
uint32_t _op_index;
|
||||
|
||||
// The identity of the source file corresponding to a machine instruction.
|
||||
uint32_t _file;
|
||||
|
||||
// A source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an
|
||||
// instruction cannot be attributed to any source line.
|
||||
uint32_t _line;
|
||||
|
||||
// A column number within a source line. Columns are numbered beginning at 1. The value 0 is reserved to indicate
|
||||
// that a statement begins at the “left edge” of the line.
|
||||
uint32_t _column;
|
||||
|
||||
// Indicates that the current instruction is a recommended breakpoint location.
|
||||
bool _is_stmt;
|
||||
|
||||
// Indicates that the current instruction is the beginning of a basic block.
|
||||
bool _basic_block;
|
||||
|
||||
// Indicates that the current address is that of the first byte after the end of a sequence of target machine
|
||||
// instructions. end_sequence terminates a sequence of lines.
|
||||
bool _end_sequence;
|
||||
|
||||
// Indicates that the current address is one (of possibly many) where execution should be suspended for an entry
|
||||
// breakpoint of a function. This field was introduced with DWARF 3.
|
||||
bool _prologue_end;
|
||||
|
||||
// Indicates that the current address is one (of possibly many) where execution should be suspended for an exit
|
||||
// breakpoint of a function. This field was introduced with DWARF 3.
|
||||
bool _epilogue_begin;
|
||||
|
||||
// Encodes the applicable instruction set architecture for the current instruction.
|
||||
// This field was introduced with DWARF 3.
|
||||
uint32_t _isa;
|
||||
|
||||
// Identifies the block to which the current instruction belongs. This field was introduced with DWARF 4.
|
||||
uint32_t _discriminator;
|
||||
|
||||
/*
|
||||
* Additional fields which are not part of the actual state as described in DWARF spec.
|
||||
*/
|
||||
// Header fields
|
||||
// Specifies which DWARF version is used in the .debug_line section. Supported version: DWARF 2, 3, and 4.
|
||||
const uint16_t _dwarf_version;
|
||||
const bool _initial_is_stmt;
|
||||
|
||||
// Implementation specific fields
|
||||
bool _append_row;
|
||||
bool _do_reset;
|
||||
bool _first_entry_in_sequence;
|
||||
bool _can_sequence_match_offset;
|
||||
bool _found_match;
|
||||
|
||||
LineNumberProgramState(const LineNumberProgramHeader& header)
|
||||
: _is_stmt(header._default_is_stmt != 0), _dwarf_version(header._version),
|
||||
_initial_is_stmt(header._default_is_stmt != 0), _found_match(false) {
|
||||
reset_fields();
|
||||
}
|
||||
|
||||
void reset_fields();
|
||||
// Defined in section 6.2.5.1 of the DWARF spec 4. add_to_address_register() must always be executed before set_index_register.
|
||||
void add_to_address_register(uint32_t operation_advance, const LineNumberProgramHeader& header);
|
||||
void set_index_register(uint32_t operation_advance, const LineNumberProgramHeader& header);
|
||||
};
|
||||
|
||||
DwarfFile* _dwarf_file;
|
||||
MarkedDwarfFileReader _reader;
|
||||
LineNumberProgramHeader _header;
|
||||
LineNumberProgramState* _state;
|
||||
const uint32_t _offset_in_library;
|
||||
const uint64_t _debug_line_offset;
|
||||
bool _is_pc_after_call;
|
||||
|
||||
bool read_header();
|
||||
bool run_line_number_program(char* filename, size_t filename_len, int* line);
|
||||
bool apply_opcode();
|
||||
bool apply_extended_opcode();
|
||||
bool apply_standard_opcode(uint8_t opcode);
|
||||
void apply_special_opcode(const uint8_t opcode);
|
||||
bool does_offset_match_entry(uintptr_t previous_address, uint32_t previous_file, uint32_t previous_line);
|
||||
void print_and_store_prev_entry(uint32_t previous_file, uint32_t previous_line);
|
||||
bool get_filename_from_header(uint32_t file_index, char* filename, size_t filename_len);
|
||||
|
||||
public:
|
||||
LineNumberProgram(DwarfFile* dwarf_file, uint32_t offset_in_library, uint64_t debug_line_offset, bool is_pc_after_call)
|
||||
: _dwarf_file(dwarf_file), _reader(dwarf_file->fd()), _offset_in_library(offset_in_library),
|
||||
_debug_line_offset(debug_line_offset), _is_pc_after_call(is_pc_after_call) {}
|
||||
|
||||
bool find_filename_and_line_number(char* filename, size_t filename_len, int* line);
|
||||
};
|
||||
|
||||
public:
|
||||
DwarfFile(const char* filepath) : ElfFile(filepath) {}
|
||||
|
||||
/*
|
||||
* Starting point of reading line number and filename information from the DWARF file.
|
||||
*
|
||||
* Given: Offset into the ELF library file, a filename buffer of size filename_size, a line number pointer.
|
||||
* Return: True: The filename is set in the 'filename' buffer and the line number at the address pointed to by 'line'.
|
||||
* False: Something went wrong either while reading from the file or during parsing due to an unexpected format.
|
||||
* This could happen if the DWARF file is in an unsupported or wrong format.
|
||||
*
|
||||
* More details about the different phases can be found at the associated methods.
|
||||
*/
|
||||
bool get_filename_and_line_number(uint32_t offset_in_library, char* filename, size_t filename_len, int* line, bool is_pc_after_call);
|
||||
};
|
||||
|
||||
#endif // !_WINDOWS && !__APPLE__
|
||||
|
||||
#endif // SHARE_UTILITIES_ELFFILE_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2022, 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
|
||||
@ -96,7 +96,7 @@ void NativeCallStack::print_on(outputStream* out, int indent) const {
|
||||
out->print("[" PTR_FORMAT "]", p2i(pc));
|
||||
}
|
||||
|
||||
if (Decoder::get_source_info(pc, buf, sizeof(buf), &line_no)) {
|
||||
if (Decoder::get_source_info(pc, buf, sizeof(buf), &line_no, frame != 0)) {
|
||||
out->print(" (%s:%d)", buf, line_no);
|
||||
}
|
||||
out->cr();
|
||||
|
@ -184,11 +184,9 @@ char* VMError::error_string(char* buf, int buflen) {
|
||||
os::current_process_id(), os::current_thread_id());
|
||||
} else if (_filename != NULL && _lineno > 0) {
|
||||
// skip directory names
|
||||
char separator = os::file_separator()[0];
|
||||
const char *p = strrchr(_filename, separator);
|
||||
int n = jio_snprintf(buf, buflen,
|
||||
"Internal Error at %s:%d, pid=%d, tid=" UINTX_FORMAT,
|
||||
p ? p + 1 : _filename, _lineno,
|
||||
get_filename_only(), _lineno,
|
||||
os::current_process_id(), os::current_thread_id());
|
||||
if (n >= 0 && n < buflen && _message) {
|
||||
if (strlen(_detail_msg) > 0) {
|
||||
@ -359,10 +357,13 @@ void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, char* bu
|
||||
while (count++ < StackPrintLimit) {
|
||||
fr.print_on_error(st, buf, buf_size);
|
||||
if (fr.pc()) { // print source file and line, if available
|
||||
char buf[128];
|
||||
char filename[128];
|
||||
int line_no;
|
||||
if (Decoder::get_source_info(fr.pc(), buf, sizeof(buf), &line_no)) {
|
||||
st->print(" (%s:%d)", buf, line_no);
|
||||
if (count == 1 && _lineno != 0) {
|
||||
// We have source information of the first frame for internal errors. There is no need to parse it from the symbols.
|
||||
st->print(" (%s:%d)", get_filename_only(), _lineno);
|
||||
} else if (Decoder::get_source_info(fr.pc(), filename, sizeof(filename), &line_no, count != 1)) {
|
||||
st->print(" (%s:%d)", filename, line_no);
|
||||
}
|
||||
}
|
||||
st->cr();
|
||||
@ -670,10 +671,8 @@ void VMError::report(outputStream* st, bool _verbose) {
|
||||
}
|
||||
if (_filename != NULL && _lineno > 0) {
|
||||
#ifdef PRODUCT
|
||||
// In product mode chop off pathname?
|
||||
char separator = os::file_separator()[0];
|
||||
const char *p = strrchr(_filename, separator);
|
||||
const char *file = p ? p+1 : _filename;
|
||||
// In product mode chop off pathname
|
||||
const char *file = get_filename_only();
|
||||
#else
|
||||
const char *file = _filename;
|
||||
#endif
|
||||
|
@ -109,6 +109,12 @@ class VMError : public AllStatic {
|
||||
char* buf, int buf_size);
|
||||
NOT_PRODUCT(private:)
|
||||
|
||||
static const char* get_filename_only() {
|
||||
char separator = os::file_separator()[0];
|
||||
const char* p = strrchr(_filename, separator);
|
||||
return p ? p+1 : _filename;
|
||||
}
|
||||
|
||||
static bool should_report_bug(unsigned int id) {
|
||||
return (id != OOM_MALLOC_ERROR) && (id != OOM_MMAP_ERROR);
|
||||
}
|
||||
|
@ -29,10 +29,11 @@
|
||||
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "concurrentTestRunner.inline.hpp"
|
||||
#include "os_linux.hpp"
|
||||
#include "unittest.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/decoder.hpp"
|
||||
|
||||
namespace {
|
||||
static void small_page_write(void* addr, size_t size) {
|
||||
@ -423,4 +424,56 @@ TEST_VM(os_linux, reserve_memory_special_concurrent) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
// Check that method JNI_CreateJavaVM is found.
|
||||
TEST(os_linux, addr_to_function_valid) {
|
||||
char buf[128] = "";
|
||||
int offset = -1;
|
||||
address valid_function_pointer = (address)JNI_CreateJavaVM;
|
||||
ASSERT_TRUE(os::dll_address_to_function_name(valid_function_pointer, buf, sizeof(buf), &offset, true));
|
||||
ASSERT_TRUE(strstr(buf, "JNI_CreateJavaVM") != nullptr);
|
||||
ASSERT_TRUE(offset >= 0);
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Test valid address of method JNI_CreateJavaVM in jni.cpp. We should get "jni.cpp" in the buffer and a valid line number.
|
||||
TEST_VM(os_linux, decoder_get_source_info_valid) {
|
||||
char buf[128] = "";
|
||||
int line = -1;
|
||||
address valid_function_pointer = (address)JNI_CreateJavaVM;
|
||||
ASSERT_TRUE(Decoder::get_source_info(valid_function_pointer, buf, sizeof(buf), &line));
|
||||
ASSERT_TRUE(strcmp(buf, "jni.cpp") == 0);
|
||||
ASSERT_TRUE(line > 0);
|
||||
}
|
||||
|
||||
// Same test as "decoder_get_source_info_valid" but with a too-small output buffer. Everything should work the same except
|
||||
// that the output buffer truncates "jni.cpp" such that we find "jni.cp" instead. The line number must be found as before.
|
||||
TEST_VM(os_linux, decoder_get_source_info_valid_truncated) {
|
||||
char buf[128] = "";
|
||||
int line = -1;
|
||||
memset(buf, 'X', sizeof(buf));
|
||||
address valid_function_pointer = (address)JNI_CreateJavaVM;
|
||||
ASSERT_TRUE(Decoder::get_source_info(valid_function_pointer, buf, 7, &line));
|
||||
ASSERT_TRUE(buf[7 - 1] == '\0');
|
||||
ASSERT_TRUE(buf[7] == 'X');
|
||||
ASSERT_TRUE(strcmp(buf, "jni.cp") == 0);
|
||||
ASSERT_TRUE(line > 0);
|
||||
}
|
||||
|
||||
// Test invalid addresses. Should not cause harm and output buffer and line must contain "" and -1, respectively.
|
||||
TEST_VM(os_linux, decoder_get_source_info_invalid) {
|
||||
char buf[128] = "";
|
||||
int line = -1;
|
||||
address invalid_function_pointers[] = { nullptr, (address)1, (address)&line };
|
||||
|
||||
for (address addr : invalid_function_pointers) {
|
||||
strcpy(buf, "somestring");
|
||||
line = 12;
|
||||
// We should return false but do not crash or fail in any way.
|
||||
ASSERT_FALSE(Decoder::get_source_info(addr, buf, sizeof(buf), &line));
|
||||
// buffer should contain "", offset should contain -1
|
||||
ASSERT_TRUE(buf[0] == '\0');
|
||||
ASSERT_TRUE(line == -1);
|
||||
}
|
||||
}
|
||||
#endif // NOT PRODUCT
|
||||
#endif // LINUX
|
||||
|
274
test/hotspot/jtreg/runtime/ErrorHandling/TestDwarf.java
Normal file
274
test/hotspot/jtreg/runtime/ErrorHandling/TestDwarf.java
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
* @bug 8242181
|
||||
* @library / /test/lib
|
||||
* @summary Test DWARF parser with various crashes if debug symbols are available. If the libjvm debug symbols are not
|
||||
* in the same directory as the libjvm.so file, in a subdirectory called .debug, or in the path specified
|
||||
* by the environment variable _JVM_DWARF_PATH, then no verification of the hs_err_file is done for libjvm.so.
|
||||
* @requires vm.debug == true & vm.flagless & vm.compMode != "Xint" & os.family == "linux" & !vm.graal.enabled & vm.gc.G1
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @run main/native/othervm -Xbootclasspath/a:. -XX:-CreateCoredumpOnCrash TestDwarf
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TestDwarf {
|
||||
static {
|
||||
System.loadLibrary("TestDwarf");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 0) {
|
||||
switch (args[0]) {
|
||||
case "unsafeAccess" -> {
|
||||
crashUnsafeAccess();
|
||||
Asserts.fail("Should crash in crashUnsafeAccess()");
|
||||
}
|
||||
case "outOfMemory" -> {
|
||||
crashOutOfMemory();
|
||||
Asserts.fail("Should crash in crashOutOfMemory()");
|
||||
}
|
||||
case "abortVMOnException" -> {
|
||||
crashAbortVmOnException();
|
||||
Asserts.fail("Should crash in crashAbortVmOnException()");
|
||||
}
|
||||
case "nativeDivByZero" -> {
|
||||
crashNativeDivByZero();
|
||||
Asserts.fail("Should crash in crashNativeDivByZero()");
|
||||
}
|
||||
case "nativeMultipleMethods" -> {
|
||||
crashNativeMultipleMethods(1);
|
||||
crashNativeMultipleMethods(2);
|
||||
crashNativeMultipleMethods(3);
|
||||
Asserts.fail("Should crash in crashNativeMultipleMethods()");
|
||||
crashNativeMultipleMethods(4);
|
||||
}
|
||||
case "nativeDereferenceNull" -> {
|
||||
crashNativeDereferenceNull();
|
||||
Asserts.fail("Should crash in crashNativeDereferenceNull()");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
test();
|
||||
} catch (UnsupportedDwarfVersionException e) {
|
||||
System.out.println("Skip test due to a DWARF section that is in an unsupported version by the parser.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crash the VM in different ways in order to verify that DWARF parsing is able to print the source information
|
||||
// in the hs_err_files for each VM and C stack frame.
|
||||
private static void test() throws Exception {
|
||||
runAndCheck(new Flags("-Xcomp", "-XX:CICrashAt=1", "--version"));
|
||||
runAndCheck(new Flags("-Xmx100M", "-XX:ErrorHandlerTest=15", "-XX:TestCrashInErrorHandler=14", "--version"));
|
||||
runAndCheck(new Flags("-XX:+CrashGCForDumpingJavaThread", "--version"));
|
||||
runAndCheck(new Flags("-Xmx10m", "-XX:+CrashOnOutOfMemoryError", TestDwarf.class.getCanonicalName(), "outOfMemory"));
|
||||
// Use -XX:-TieredCompilation as C1 is currently not aborting the VM (JDK-8264899).
|
||||
runAndCheck(new Flags(TestDwarf.class.getCanonicalName(), "unsafeAccess"));
|
||||
runAndCheck(new Flags("-XX:-TieredCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:AbortVMOnException=MyException",
|
||||
TestDwarf.class.getCanonicalName(), "abortVMOnException"));
|
||||
if (Platform.isX64() || Platform.isX86()) {
|
||||
// Not all platforms raise SIGFPE but x86_32 and x86_64 do.
|
||||
runAndCheck(new Flags(TestDwarf.class.getCanonicalName(), "nativeDivByZero"),
|
||||
new DwarfConstraint(0, "Java_TestDwarf_crashNativeDivByZero", "libTestDwarf.c", 59));
|
||||
runAndCheck(new Flags(TestDwarf.class.getCanonicalName(), "nativeMultipleMethods"),
|
||||
new DwarfConstraint(0, "foo", "libTestDwarf.c", 42),
|
||||
new DwarfConstraint(1, "Java_TestDwarf_crashNativeMultipleMethods", "libTestDwarf.c", 70));
|
||||
}
|
||||
runAndCheck(new Flags(TestDwarf.class.getCanonicalName(), "nativeDereferenceNull"),
|
||||
new DwarfConstraint(0, "dereference_null", "libTestDwarfHelper.h", 44));
|
||||
}
|
||||
|
||||
private static void runAndCheck(Flags flags, DwarfConstraint... constraints) throws Exception {
|
||||
OutputAnalyzer crashOut;
|
||||
crashOut = ProcessTools.executeProcess(ProcessTools.createTestJvm(flags.getFlags()));
|
||||
String crashOutputString = crashOut.getOutput();
|
||||
Asserts.assertNotEquals(crashOut.getExitValue(), 0, "Crash JVM should not exit gracefully");
|
||||
Pattern pattern = Pattern.compile("hs_err_pid[0-9]*.log");
|
||||
Matcher matcher = pattern.matcher(crashOutputString);
|
||||
System.out.println(crashOutputString);
|
||||
if (matcher.find()) {
|
||||
String hsErrFileName = matcher.group();
|
||||
System.out.println("hs_err_file: " + hsErrFileName);
|
||||
File hs_err_file = new File(hsErrFileName);
|
||||
BufferedReader reader = new BufferedReader(new FileReader(hs_err_file));
|
||||
String line;
|
||||
boolean foundNativeFrames = false;
|
||||
int matches = 0;
|
||||
int frameIdx = 0;
|
||||
// Check all stack entries after the line starting with "Native frames" in the hs_err_file until an empty line
|
||||
// is found which denotes the end of the stack frames.
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (foundNativeFrames) {
|
||||
if (line.isEmpty()) {
|
||||
// Done with the entire stack.
|
||||
break;
|
||||
} else if ((line.startsWith("C") || line.startsWith("V"))) {
|
||||
// Could be VM or native C frame. There are usually no symbols available for libpthread.so.
|
||||
matches++;
|
||||
// File and library names are non-empty and may contain English letters, underscores, dots or numbers ([a-zA-Z0-9_.]+).
|
||||
// Line numbers have at least one digit and start with non-zero ([1-9][0-9]*).
|
||||
pattern = Pattern.compile("[CV][\\s\\t]+\\[([a-zA-Z0-9_.]+)\\+0x.+][\\s\\t]+.*\\+0x.+[\\s\\t]+\\([a-zA-Z0-9_.]+\\.[a-z]+:[1-9][0-9]*\\)");
|
||||
matcher = pattern.matcher(line);
|
||||
if (!matcher.find()) {
|
||||
checkNoSourceLine(crashOutputString, line);
|
||||
}
|
||||
|
||||
// Check additional DWARF constraints
|
||||
if (constraints != null) {
|
||||
int finalFrameIdx = frameIdx;
|
||||
String finalLine = line;
|
||||
Arrays.stream(constraints).forEach(c -> c.checkConstraint(finalFrameIdx, finalLine));
|
||||
}
|
||||
}
|
||||
frameIdx++;
|
||||
} else if (line.startsWith("Native frames")) {
|
||||
// Stack starts after this line.
|
||||
foundNativeFrames = true;
|
||||
}
|
||||
}
|
||||
Asserts.assertGreaterThan(matches, 0, "Could not find any stack frames");
|
||||
} else {
|
||||
throw new RuntimeException("Could not find an hs_err_file");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There are some valid cases where we cannot find source information. Check these.
|
||||
*/
|
||||
private static void checkNoSourceLine(String crashOutputString, String line) {
|
||||
Pattern pattern = Pattern.compile("[CV][\\s\\t]+\\[([a-zA-Z0-9_.]+)\\+0x.+][\\s\\t]+.*\\+0x");
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
Asserts.assertTrue(matcher.find(), "Must find library in \"" + line + "\"");
|
||||
// Check if there are symbols available for library. If not, then we cannot find any source information for this library.
|
||||
// This can happen if this test is run without any JDK debug symbols at all but also for some libraries like libpthread.so
|
||||
// which usually has no symbols available.
|
||||
String library = matcher.group(1);
|
||||
pattern = Pattern.compile("Failed to load DWARF file for library.*" + library + ".*or find DWARF sections directly inside it");
|
||||
matcher = pattern.matcher(crashOutputString);
|
||||
if (!matcher.find()) {
|
||||
bailoutIfUnsupportedDwarfVersion(crashOutputString);
|
||||
Asserts.fail("Could not find filename or line number in \"" + line + "\"");
|
||||
}
|
||||
// We should always find symbols for libTestDwarf.so.
|
||||
Asserts.assertFalse(library.equals("libTestDwarf.so"), "Could not find filename or line number in \"" + line + "\" for libTestDwarf.so");
|
||||
System.out.println("Did not find symbols for " + library + ". If they are not in the same directory as " + library + " consider setting " +
|
||||
"the environmental variable _JVM_DWARF_PATH to point to the debug symbols directory.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Some older GCC versions might emit DWARF sections in an old format that is not supported by the DWARF parser.
|
||||
* If this is the case, skip this entire test by throwing UnsupportedDwarfVersionException.
|
||||
*/
|
||||
private static void bailoutIfUnsupportedDwarfVersion(String crashOutputString) {
|
||||
Pattern pattern = Pattern.compile(".debug_\\S+ in unsupported DWARF version \\d+");
|
||||
Matcher matcher = pattern.matcher(crashOutputString);
|
||||
if (matcher.find()) {
|
||||
throw new UnsupportedDwarfVersionException();
|
||||
}
|
||||
}
|
||||
|
||||
// Crash with SIGSEGV.
|
||||
private static void crashUnsafeAccess() throws Exception {
|
||||
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
Unsafe unsafe = (Unsafe)f.get(null);
|
||||
unsafe.putAddress(0, 0); // Crash
|
||||
}
|
||||
|
||||
// Crash with Internal Error: Java heap space.
|
||||
private static void crashOutOfMemory() {
|
||||
Object[] o = null;
|
||||
|
||||
// Loop endlessly and consume memory until we run out. Will crash due to -XX:+CrashOnOutOfMemoryError.
|
||||
while (true) {
|
||||
o = new Object[] {o};
|
||||
}
|
||||
}
|
||||
|
||||
// Crash with Internal Error: Saw java.lang.RuntimeException, aborting.
|
||||
// Crash happens due to an exception raised in combination with -XX:AbortVMOnException.
|
||||
private static void crashAbortVmOnException() {
|
||||
throw new MyException();
|
||||
}
|
||||
|
||||
private static native void crashNativeDivByZero();
|
||||
private static native void crashNativeDereferenceNull();
|
||||
private static native void crashNativeMultipleMethods(int x);
|
||||
}
|
||||
|
||||
class UnsupportedDwarfVersionException extends RuntimeException { }
|
||||
|
||||
class MyException extends RuntimeException { }
|
||||
|
||||
class Flags {
|
||||
private final List<String> listOfOptions = new ArrayList<>();
|
||||
|
||||
Flags(String... flags) {
|
||||
listOfOptions.add("-XX:TraceDwarfLevel=2"); // Always add debug flag
|
||||
listOfOptions.add("-XX:-CreateCoredumpOnCrash"); // Never create dumps
|
||||
listOfOptions.addAll(Arrays.asList(flags));
|
||||
}
|
||||
|
||||
public List<String> getFlags() {
|
||||
return listOfOptions;
|
||||
}
|
||||
|
||||
}
|
||||
class DwarfConstraint {
|
||||
private final int frameIdx;
|
||||
private final String methodName;
|
||||
private final String dwarfInfo;
|
||||
|
||||
DwarfConstraint(int frameIdx, String methodName, String fileName, int lineNo) {
|
||||
this.frameIdx = frameIdx;
|
||||
this.methodName = methodName;
|
||||
this.dwarfInfo = "(" + fileName + ":" + lineNo + ")";
|
||||
}
|
||||
|
||||
public void checkConstraint(int currentFrameIdx, String line) {
|
||||
if (frameIdx == currentFrameIdx) {
|
||||
Asserts.assertTrue(line.contains(methodName), "Could not find method name " + methodName + " in \"" + line + "\"");
|
||||
Asserts.assertTrue(line.contains(dwarfInfo) , "Could not find DWARF info " + dwarfInfo + " in \"" + line + "\"");
|
||||
}
|
||||
}
|
||||
}
|
78
test/hotspot/jtreg/runtime/ErrorHandling/libTestDwarf.c
Normal file
78
test/hotspot/jtreg/runtime/ErrorHandling/libTestDwarf.c
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
#include "jni.h"
|
||||
#include "libTestDwarfHelper.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int zero = 0;
|
||||
int result = 0;
|
||||
int limit = 20;
|
||||
|
||||
// Just big enough by doing some random things such that it is not inlined.
|
||||
void foo(int x) {
|
||||
printf("foo3:");
|
||||
printf(" %d\n", x);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
result += zero + i;
|
||||
}
|
||||
if (x == 3) {
|
||||
for (int i = 0; i < limit; i++) {
|
||||
result -= zero + i;
|
||||
}
|
||||
result = 3 / zero; // Crash
|
||||
} else {
|
||||
for (int i = 0; i < limit; i++) {
|
||||
result -= zero + i;
|
||||
}
|
||||
result = 3 / 2; // No crash
|
||||
}
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
for (int j = zero; j < limit; j++) {
|
||||
result += zero - i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_TestDwarf_crashNativeDivByZero(JNIEnv* env, jclass jclazz) {
|
||||
limit = 21;
|
||||
foo(34 / zero); // Crash
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_TestDwarf_crashNativeDereferenceNull(JNIEnv* env, jclass jclazz) {
|
||||
dereference_null();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_TestDwarf_crashNativeMultipleMethods(JNIEnv* env, jclass jclazz, jint x) {
|
||||
// foo() is not inlined
|
||||
foo(x - 2);
|
||||
foo(x - 1);
|
||||
foo(x);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
result += zero + i;
|
||||
}
|
||||
for (int i = 0; i < limit; i++) {
|
||||
result += zero + i;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void unused1() {
|
||||
}
|
||||
|
||||
void unused2() {
|
||||
}
|
||||
|
||||
void unused3() {
|
||||
}
|
||||
|
||||
void unused4() {
|
||||
}
|
||||
|
||||
void unused5() {
|
||||
}
|
||||
|
||||
void dereference_null() {
|
||||
int* x = (int*)0;
|
||||
*x = 34; // Crash
|
||||
}
|
Loading…
Reference in New Issue
Block a user