| File: | jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp |
| Warning: | line 419, column 16 Value stored to 'repository_path_len' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #include "precompiled.hpp" |
| 26 | #include "jvm_io.h" |
| 27 | #include "jfr/jfrEvents.hpp" |
| 28 | #include "jfr/jni/jfrJavaSupport.hpp" |
| 29 | #include "jfr/leakprofiler/leakProfiler.hpp" |
| 30 | #include "jfr/recorder/repository/jfrEmergencyDump.hpp" |
| 31 | #include "jfr/recorder/service/jfrPostBox.hpp" |
| 32 | #include "jfr/recorder/service/jfrRecorderService.hpp" |
| 33 | #include "jfr/utilities/jfrTypes.hpp" |
| 34 | #include "logging/log.hpp" |
| 35 | #include "runtime/arguments.hpp" |
| 36 | #include "runtime/atomic.hpp" |
| 37 | #include "runtime/globals.hpp" |
| 38 | #include "runtime/mutexLocker.hpp" |
| 39 | #include "runtime/os.hpp" |
| 40 | #include "runtime/thread.inline.hpp" |
| 41 | #include "utilities/growableArray.hpp" |
| 42 | #include "utilities/ostream.hpp" |
| 43 | |
| 44 | char JfrEmergencyDump::_dump_path[JVM_MAXPATHLEN4096 + 1] = { 0 }; |
| 45 | |
| 46 | static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr"; |
| 47 | static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr"; |
| 48 | static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr"; |
| 49 | static const char chunk_file_jfr_ext[] = ".jfr"; |
| 50 | static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS" (note: we just use a subset of the full timestamp) |
| 51 | static fio_fd emergency_fd = invalid_fd; |
| 52 | static const int64_t chunk_file_header_size = 68; |
| 53 | static const size_t chunk_file_extension_length = sizeof chunk_file_jfr_ext - 1; |
| 54 | |
| 55 | /* |
| 56 | * The emergency dump logic is restrictive when it comes to |
| 57 | * using internal VM constructs such as ResourceArea / Handle / Arena. |
| 58 | * The reason being that the thread context is unknown. |
| 59 | * |
| 60 | * A single static buffer of size JVM_MAXPATHLEN is used for building paths. |
| 61 | * os::malloc / os::free are used in a few places. |
| 62 | */ |
| 63 | |
| 64 | static char _path_buffer[JVM_MAXPATHLEN4096 + 1] = { 0 }; |
| 65 | |
| 66 | static bool is_path_empty() { |
| 67 | return *_path_buffer == '\0'; |
| 68 | } |
| 69 | |
| 70 | // returns with an appended file separator (if successful) |
| 71 | static size_t get_dump_directory() { |
| 72 | const char* dump_path = JfrEmergencyDump::get_dump_path(); |
| 73 | if (*dump_path == '\0') { |
| 74 | if (os::get_current_directory(_path_buffer, sizeof(_path_buffer)) == NULL__null) { |
| 75 | return 0; |
| 76 | } |
| 77 | } else { |
| 78 | strcpy(_path_buffer, dump_path); |
| 79 | } |
| 80 | const size_t path_len = strlen(_path_buffer); |
| 81 | const int result = jio_snprintf(_path_buffer + path_len, |
| 82 | sizeof(_path_buffer), |
| 83 | "%s", |
| 84 | os::file_separator()); |
| 85 | return (result == -1) ? 0 : strlen(_path_buffer); |
| 86 | } |
| 87 | |
| 88 | static fio_fd open_exclusivly(const char* path) { |
| 89 | assert((path != NULL) && (*path != '\0'), "invariant")do { if (!((path != __null) && (*path != '\0'))) { (* g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 89, "assert(" "(path != __null) && (*path != '\\0')" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 90 | return os::open(path, O_CREAT0100 | O_RDWR02, S_IREAD0400 | S_IWRITE0200); |
| 91 | } |
| 92 | |
| 93 | static bool is_emergency_dump_file_open() { |
| 94 | return emergency_fd != invalid_fd; |
| 95 | } |
| 96 | |
| 97 | static bool open_emergency_dump_fd(const char* path) { |
| 98 | if (path == NULL__null) { |
| 99 | return false; |
| 100 | } |
| 101 | assert(emergency_fd == invalid_fd, "invariant")do { if (!(emergency_fd == invalid_fd)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 101, "assert(" "emergency_fd == invalid_fd" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 102 | emergency_fd = open_exclusivly(path); |
| 103 | return emergency_fd != invalid_fd; |
| 104 | } |
| 105 | |
| 106 | static void close_emergency_dump_file() { |
| 107 | if (is_emergency_dump_file_open()) { |
| 108 | os::close(emergency_fd); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | static const char* create_emergency_dump_path() { |
| 113 | assert(is_path_empty(), "invariant")do { if (!(is_path_empty())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 113, "assert(" "is_path_empty()" ") failed", "invariant"); :: breakpoint(); } } while (0); |
| 114 | |
| 115 | const size_t path_len = get_dump_directory(); |
| 116 | if (path_len == 0) { |
| 117 | return NULL__null; |
| 118 | } |
| 119 | const char* filename_fmt = NULL__null; |
| 120 | // fetch specific error cause |
| 121 | switch (JfrJavaSupport::cause()) { |
| 122 | case JfrJavaSupport::OUT_OF_MEMORY: |
| 123 | filename_fmt = vm_oom_filename_fmt; |
| 124 | break; |
| 125 | case JfrJavaSupport::STACK_OVERFLOW: |
| 126 | filename_fmt = vm_soe_filename_fmt; |
| 127 | break; |
| 128 | default: |
| 129 | filename_fmt = vm_error_filename_fmt; |
| 130 | } |
| 131 | const bool result = Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), _path_buffer + path_len, JVM_MAXPATHLEN4096 + 1 - path_len); |
| 132 | return result ? _path_buffer : NULL__null; |
| 133 | } |
| 134 | |
| 135 | bool JfrEmergencyDump::open_emergency_dump_file() { |
| 136 | if (is_emergency_dump_file_open()) { |
| 137 | // opened already |
| 138 | return true; |
| 139 | } |
| 140 | |
| 141 | bool result = open_emergency_dump_fd(create_emergency_dump_path()); |
| 142 | if (!result && *_dump_path != '\0') { |
| 143 | log_warning(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Warning))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Warning>("Unable to create an emergency dump file at the location set by dumppath=%s", _dump_path); |
| 144 | // Fallback. Try to create it in the current directory. |
| 145 | *_dump_path = '\0'; |
| 146 | *_path_buffer = '\0'; |
| 147 | result = open_emergency_dump_fd(create_emergency_dump_path()); |
| 148 | } |
| 149 | return result; |
| 150 | } |
| 151 | |
| 152 | static void report(outputStream* st, bool emergency_file_opened, const char* repository_path) { |
| 153 | assert(st != NULL, "invariant")do { if (!(st != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 153, "assert(" "st != __null" ") failed", "invariant"); ::breakpoint (); } } while (0); |
| 154 | if (emergency_file_opened) { |
| 155 | st->print_raw("# JFR recording file will be written. Location: "); |
| 156 | st->print_raw_cr(_path_buffer); |
| 157 | st->print_raw_cr("#"); |
| 158 | } else if (repository_path != NULL__null) { |
| 159 | st->print_raw("# The JFR repository may contain useful JFR files. Location: "); |
| 160 | st->print_raw_cr(repository_path); |
| 161 | st->print_raw_cr("#"); |
| 162 | } else if (!is_path_empty()) { |
| 163 | st->print_raw("# Unable to create a JFR recording file at location: "); |
| 164 | st->print_raw_cr(_path_buffer); |
| 165 | st->print_raw_cr("#"); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | void JfrEmergencyDump::set_dump_path(const char* dump_path) { |
| 170 | if (dump_path == NULL__null || *dump_path == '\0') { |
| 171 | os::get_current_directory(_dump_path, sizeof(_dump_path)); |
| 172 | } else { |
| 173 | if (strlen(dump_path) < JVM_MAXPATHLEN4096 + 1) { |
| 174 | strncpy(_dump_path, dump_path, JVM_MAXPATHLEN4096 + 1); |
| 175 | _dump_path[JVM_MAXPATHLEN4096 + 1 - 1] = '\0'; |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | const char* JfrEmergencyDump::get_dump_path() { |
| 181 | return _dump_path; |
| 182 | } |
| 183 | |
| 184 | void JfrEmergencyDump::on_vm_error_report(outputStream* st, const char* repository_path) { |
| 185 | assert(st != NULL, "invariant")do { if (!(st != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 185, "assert(" "st != __null" ") failed", "invariant"); ::breakpoint (); } } while (0); |
| 186 | Thread* thread = Thread::current_or_null_safe(); |
| 187 | if (thread != NULL__null) { |
| 188 | report(st, open_emergency_dump_file(), repository_path); |
| 189 | } else if (repository_path != NULL__null) { |
| 190 | // a non-attached thread will not be able to write anything later |
| 191 | report(st, false, repository_path); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | static int file_sort(const char** const file1, const char** file2) { |
| 196 | assert(NULL != *file1 && NULL != *file2, "invariant")do { if (!(__null != *file1 && __null != *file2)) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 196, "assert(" "__null != *file1 && __null != *file2" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 197 | int cmp = strncmp(*file1, *file2, iso8601_len); |
| 198 | if (0 == cmp) { |
| 199 | const char* const dot1 = strchr(*file1, '.'); |
| 200 | assert(NULL != dot1, "invariant")do { if (!(__null != dot1)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 200, "assert(" "__null != dot1" ") failed", "invariant"); :: breakpoint(); } } while (0); |
| 201 | const char* const dot2 = strchr(*file2, '.'); |
| 202 | assert(NULL != dot2, "invariant")do { if (!(__null != dot2)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 202, "assert(" "__null != dot2" ") failed", "invariant"); :: breakpoint(); } } while (0); |
| 203 | ptrdiff_t file1_len = dot1 - *file1; |
| 204 | ptrdiff_t file2_len = dot2 - *file2; |
| 205 | if (file1_len < file2_len) { |
| 206 | return -1; |
| 207 | } |
| 208 | if (file1_len > file2_len) { |
| 209 | return 1; |
| 210 | } |
| 211 | assert(file1_len == file2_len, "invariant")do { if (!(file1_len == file2_len)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 211, "assert(" "file1_len == file2_len" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 212 | cmp = strncmp(*file1, *file2, file1_len); |
| 213 | } |
| 214 | assert(cmp != 0, "invariant")do { if (!(cmp != 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 214, "assert(" "cmp != 0" ") failed", "invariant"); ::breakpoint (); } } while (0); |
| 215 | return cmp; |
| 216 | } |
| 217 | |
| 218 | static void iso8601_to_date_time(char* iso8601_str) { |
| 219 | assert(iso8601_str != NULL, "invariant")do { if (!(iso8601_str != __null)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 219, "assert(" "iso8601_str != __null" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 220 | assert(strlen(iso8601_str) == iso8601_len, "invariant")do { if (!(strlen(iso8601_str) == iso8601_len)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 220, "assert(" "strlen(iso8601_str) == iso8601_len" ") failed" , "invariant"); ::breakpoint(); } } while (0); |
| 221 | // "YYYY-MM-DDTHH:MM:SS" |
| 222 | for (size_t i = 0; i < iso8601_len; ++i) { |
| 223 | switch (iso8601_str[i]) { |
| 224 | case 'T': |
| 225 | case '-': |
| 226 | case ':': |
| 227 | iso8601_str[i] = '_'; |
| 228 | break; |
| 229 | } |
| 230 | } |
| 231 | // "YYYY_MM_DD_HH_MM_SS" |
| 232 | } |
| 233 | |
| 234 | static void date_time(char* buffer, size_t buffer_len) { |
| 235 | assert(buffer != NULL, "invariant")do { if (!(buffer != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 235, "assert(" "buffer != __null" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 236 | assert(buffer_len >= iso8601_len, "buffer too small")do { if (!(buffer_len >= iso8601_len)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 236, "assert(" "buffer_len >= iso8601_len" ") failed", "buffer too small" ); ::breakpoint(); } } while (0); |
| 237 | os::iso8601_time(buffer, buffer_len); |
| 238 | assert(strlen(buffer) >= iso8601_len + 1, "invariant")do { if (!(strlen(buffer) >= iso8601_len + 1)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 238, "assert(" "strlen(buffer) >= iso8601_len + 1" ") failed" , "invariant"); ::breakpoint(); } } while (0); |
| 239 | // "YYYY-MM-DDTHH:MM:SS" |
| 240 | buffer[iso8601_len] = '\0'; |
| 241 | iso8601_to_date_time(buffer); |
| 242 | } |
| 243 | |
| 244 | static int64_t file_size(fio_fd fd) { |
| 245 | assert(fd != invalid_fd, "invariant")do { if (!(fd != invalid_fd)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 245, "assert(" "fd != invalid_fd" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 246 | const int64_t current_offset = os::current_file_offset(fd); |
| 247 | const int64_t size = os::lseek(fd, 0, SEEK_END2); |
| 248 | os::seek_to_file_offset(fd, current_offset); |
| 249 | return size; |
| 250 | } |
| 251 | |
| 252 | class RepositoryIterator : public StackObj { |
| 253 | private: |
| 254 | GrowableArray<const char*>* _file_names; |
| 255 | int _path_buffer_file_name_offset; |
| 256 | mutable int _iterator; |
| 257 | const char* fully_qualified(const char* file_name) const; |
| 258 | const char* filter(const char* file_name) const; |
| 259 | public: |
| 260 | RepositoryIterator(const char* repository_path); |
| 261 | ~RepositoryIterator(); |
| 262 | bool has_next() const; |
| 263 | const char* next() const; |
| 264 | }; |
| 265 | |
| 266 | // append the file_name at the _path_buffer_file_name_offset position |
| 267 | const char* RepositoryIterator::fully_qualified(const char* file_name) const { |
| 268 | assert(NULL != file_name, "invariant")do { if (!(__null != file_name)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 268, "assert(" "__null != file_name" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 269 | assert(!is_path_empty(), "invariant")do { if (!(!is_path_empty())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 269, "assert(" "!is_path_empty()" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 270 | assert(_path_buffer_file_name_offset != 0, "invariant")do { if (!(_path_buffer_file_name_offset != 0)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 270, "assert(" "_path_buffer_file_name_offset != 0" ") failed" , "invariant"); ::breakpoint(); } } while (0); |
| 271 | |
| 272 | const int result = jio_snprintf(_path_buffer + _path_buffer_file_name_offset, |
| 273 | sizeof(_path_buffer) - _path_buffer_file_name_offset, |
| 274 | "%s", |
| 275 | file_name); |
| 276 | return result != -1 ? _path_buffer : NULL__null; |
| 277 | } |
| 278 | |
| 279 | // caller responsible for deallocation |
| 280 | const char* RepositoryIterator::filter(const char* file_name) const { |
| 281 | if (file_name == NULL__null) { |
| 282 | return NULL__null; |
| 283 | } |
| 284 | const size_t len = strlen(file_name); |
| 285 | if ((len < chunk_file_extension_length) || |
| 286 | (strncmp(&file_name[len - chunk_file_extension_length], |
| 287 | chunk_file_jfr_ext, |
| 288 | chunk_file_extension_length) != 0)) { |
| 289 | // not a .jfr file |
| 290 | return NULL__null; |
| 291 | } |
| 292 | const char* fqn = fully_qualified(file_name); |
| 293 | if (fqn == NULL__null) { |
| 294 | return NULL__null; |
| 295 | } |
| 296 | const fio_fd fd = open_exclusivly(fqn); |
| 297 | if (invalid_fd == fd) { |
| 298 | return NULL__null; |
| 299 | } |
| 300 | const int64_t size = file_size(fd); |
| 301 | os::close(fd); |
| 302 | if (size <= chunk_file_header_size) { |
| 303 | return NULL__null; |
| 304 | } |
| 305 | char* const file_name_copy = (char*)os::malloc(len + 1, mtTracing); |
| 306 | if (file_name_copy == NULL__null) { |
| 307 | log_error(jfr, system)(!(LogImpl<(LogTag::_jfr), (LogTag::_system), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::_system), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Unable to malloc memory during jfr emergency dump"); |
| 308 | return NULL__null; |
| 309 | } |
| 310 | strncpy(file_name_copy, file_name, len + 1); |
| 311 | return file_name_copy; |
| 312 | } |
| 313 | |
| 314 | RepositoryIterator::RepositoryIterator(const char* repository_path) : |
| 315 | _file_names(NULL__null), |
| 316 | _path_buffer_file_name_offset(0), |
| 317 | _iterator(0) { |
| 318 | DIR* dirp = os::opendir(repository_path); |
| 319 | if (dirp == NULL__null) { |
| 320 | log_error(jfr, system)(!(LogImpl<(LogTag::_jfr), (LogTag::_system), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::_system), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Unable to open repository %s", repository_path); |
| 321 | return; |
| 322 | } |
| 323 | // store repository path in the path buffer and save that position |
| 324 | _path_buffer_file_name_offset = jio_snprintf(_path_buffer, |
| 325 | sizeof(_path_buffer), |
| 326 | "%s%s", |
| 327 | repository_path, |
| 328 | os::file_separator()); |
| 329 | if (_path_buffer_file_name_offset == -1) { |
| 330 | return; |
| 331 | } |
| 332 | _file_names = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<const char*>(10, mtTracing); |
| 333 | if (_file_names == NULL__null) { |
| 334 | log_error(jfr, system)(!(LogImpl<(LogTag::_jfr), (LogTag::_system), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::_system), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Unable to malloc memory during jfr emergency dump"); |
| 335 | return; |
| 336 | } |
| 337 | // iterate files in the repository and append filtered file names to the files array |
| 338 | struct dirent* dentry; |
| 339 | while ((dentry = os::readdir(dirp)) != NULL__null) { |
| 340 | const char* file_name = filter(dentry->d_name); |
| 341 | if (file_name != NULL__null) { |
| 342 | _file_names->append(file_name); |
| 343 | } |
| 344 | } |
| 345 | os::closedir(dirp); |
| 346 | if (_file_names->length() > 1) { |
| 347 | _file_names->sort(file_sort); |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | RepositoryIterator::~RepositoryIterator() { |
| 352 | if (_file_names != NULL__null) { |
| 353 | for (int i = 0; i < _file_names->length(); ++i) { |
| 354 | os::free(const_cast<char*>(_file_names->at(i))); |
| 355 | } |
| 356 | delete _file_names; |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | bool RepositoryIterator::has_next() const { |
| 361 | return _file_names != NULL__null && _iterator < _file_names->length(); |
| 362 | } |
| 363 | |
| 364 | const char* RepositoryIterator::next() const { |
| 365 | return _iterator >= _file_names->length() ? NULL__null : fully_qualified(_file_names->at(_iterator++)); |
| 366 | } |
| 367 | |
| 368 | static void write_repository_files(const RepositoryIterator& iterator, char* const copy_block, size_t block_size) { |
| 369 | assert(is_emergency_dump_file_open(), "invariant")do { if (!(is_emergency_dump_file_open())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 369, "assert(" "is_emergency_dump_file_open()" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 370 | while (iterator.has_next()) { |
| 371 | fio_fd current_fd = invalid_fd; |
| 372 | const char* const fqn = iterator.next(); |
| 373 | assert(fqn != NULL, "invariant")do { if (!(fqn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 373, "assert(" "fqn != __null" ") failed", "invariant"); :: breakpoint(); } } while (0); |
| 374 | current_fd = open_exclusivly(fqn); |
| 375 | if (current_fd != invalid_fd) { |
| 376 | const int64_t size = file_size(current_fd); |
| 377 | assert(size > 0, "invariant")do { if (!(size > 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 377, "assert(" "size > 0" ") failed", "invariant"); ::breakpoint (); } } while (0); |
| 378 | int64_t bytes_read = 0; |
| 379 | int64_t bytes_written = 0; |
| 380 | while (bytes_read < size) { |
| 381 | const ssize_t read_result = os::read_at(current_fd, copy_block, (int)block_size, bytes_read); |
| 382 | if (-1 == read_result) { |
| 383 | log_info(jfr)(!(LogImpl<(LogTag::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Info))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Info>( // For user, should not be "jfr, system" |
| 384 | "Unable to recover JFR data"); |
| 385 | break; |
| 386 | } |
| 387 | bytes_read += (int64_t)read_result; |
| 388 | assert(bytes_read - bytes_written <= (int64_t)block_size, "invariant")do { if (!(bytes_read - bytes_written <= (int64_t)block_size )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 388, "assert(" "bytes_read - bytes_written <= (int64_t)block_size" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 389 | bytes_written += (int64_t)os::write(emergency_fd, copy_block, bytes_read - bytes_written); |
| 390 | assert(bytes_read == bytes_written, "invariant")do { if (!(bytes_read == bytes_written)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 390, "assert(" "bytes_read == bytes_written" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 391 | } |
| 392 | os::close(current_fd); |
| 393 | } |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | static void write_emergency_dump_file(const RepositoryIterator& iterator) { |
| 398 | static const size_t block_size = 1 * M; // 1 mb |
| 399 | char* const copy_block = (char*)os::malloc(block_size, mtTracing); |
| 400 | if (copy_block == NULL__null) { |
| 401 | log_error(jfr, system)(!(LogImpl<(LogTag::_jfr), (LogTag::_system), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::_system), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Unable to malloc memory during jfr emergency dump"); |
| 402 | log_error(jfr, system)(!(LogImpl<(LogTag::_jfr), (LogTag::_system), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::is_level(LogLevel::Error))) ? (void)0 : LogImpl<(LogTag ::_jfr), (LogTag::_system), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG)>::write<LogLevel ::Error>("Unable to write jfr emergency dump file"); |
| 403 | } else { |
| 404 | write_repository_files(iterator, copy_block, block_size); |
| 405 | os::free(copy_block); |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | void JfrEmergencyDump::on_vm_error(const char* repository_path) { |
| 410 | assert(repository_path != NULL, "invariant")do { if (!(repository_path != __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 410, "assert(" "repository_path != __null" ") failed", "invariant" ); ::breakpoint(); } } while (0); |
| 411 | if (open_emergency_dump_file()) { |
| 412 | RepositoryIterator iterator(repository_path); |
| 413 | write_emergency_dump_file(iterator); |
| 414 | close_emergency_dump_file(); |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | static const char* create_emergency_chunk_path(const char* repository_path) { |
| 419 | const size_t repository_path_len = strlen(repository_path); |
Value stored to 'repository_path_len' during its initialization is never read | |
| 420 | char date_time_buffer[32] = { 0 }; |
| 421 | date_time(date_time_buffer, sizeof(date_time_buffer)); |
| 422 | // append the individual substrings |
| 423 | const int result = jio_snprintf(_path_buffer, |
| 424 | JVM_MAXPATHLEN4096 + 1, |
| 425 | "%s%s%s%s", |
| 426 | repository_path, |
| 427 | os::file_separator(), |
| 428 | date_time_buffer, |
| 429 | chunk_file_jfr_ext); |
| 430 | return result == -1 ? NULL__null : _path_buffer; |
| 431 | } |
| 432 | |
| 433 | const char* JfrEmergencyDump::chunk_path(const char* repository_path) { |
| 434 | if (repository_path == NULL__null) { |
| 435 | if (!open_emergency_dump_file()) { |
| 436 | return NULL__null; |
| 437 | } |
| 438 | // We can directly use the emergency dump file name as the chunk. |
| 439 | // The chunk writer will open its own fd so we close this descriptor. |
| 440 | close_emergency_dump_file(); |
| 441 | assert(!is_path_empty(), "invariant")do { if (!(!is_path_empty())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 441, "assert(" "!is_path_empty()" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 442 | return _path_buffer; |
| 443 | } |
| 444 | return create_emergency_chunk_path(repository_path); |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | * We are just about to exit the VM, so we will be very aggressive |
| 449 | * at this point in order to increase overall success of dumping jfr data. |
| 450 | * |
| 451 | * If we end up deadlocking in the attempt of dumping out jfr data, |
| 452 | * we rely on the WatcherThread task "is_error_reported()", |
| 453 | * to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump). |
| 454 | * This "safety net" somewhat explains the aggressiveness in this attempt. |
| 455 | * |
| 456 | */ |
| 457 | static bool prepare_for_emergency_dump(Thread* thread) { |
| 458 | assert(thread != NULL, "invariant")do { if (!(thread != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 458, "assert(" "thread != __null" ") failed", "invariant"); ::breakpoint(); } } while (0); |
| 459 | if (thread->is_Watcher_thread()) { |
| 460 | // need WatcherThread as a safeguard against potential deadlocks |
| 461 | return false; |
| 462 | } |
| 463 | |
| 464 | #ifdef ASSERT1 |
| 465 | Mutex* owned_lock = thread->owned_locks(); |
| 466 | while (owned_lock != NULL__null) { |
| 467 | Mutex* next = owned_lock->next(); |
| 468 | owned_lock->unlock(); |
| 469 | owned_lock = next; |
| 470 | } |
| 471 | #endif // ASSERT |
| 472 | |
| 473 | if (Threads_lock->owned_by_self()) { |
| 474 | Threads_lock->unlock(); |
| 475 | } |
| 476 | |
| 477 | if (Module_lock->owned_by_self()) { |
| 478 | Module_lock->unlock(); |
| 479 | } |
| 480 | |
| 481 | if (ClassLoaderDataGraph_lock->owned_by_self()) { |
| 482 | ClassLoaderDataGraph_lock->unlock(); |
| 483 | } |
| 484 | |
| 485 | if (Heap_lock->owned_by_self()) { |
| 486 | Heap_lock->unlock(); |
| 487 | } |
| 488 | |
| 489 | if (VMOperation_lock->owned_by_self()) { |
| 490 | VMOperation_lock->unlock(); |
| 491 | } |
| 492 | |
| 493 | if (Service_lock->owned_by_self()) { |
| 494 | Service_lock->unlock(); |
| 495 | } |
| 496 | |
| 497 | if (UseNotificationThread && Notification_lock->owned_by_self()) { |
| 498 | Notification_lock->unlock(); |
| 499 | } |
| 500 | |
| 501 | if (CodeCache_lock->owned_by_self()) { |
| 502 | CodeCache_lock->unlock(); |
| 503 | } |
| 504 | |
| 505 | if (PeriodicTask_lock->owned_by_self()) { |
| 506 | PeriodicTask_lock->unlock(); |
| 507 | } |
| 508 | |
| 509 | if (JfrMsg_lock->owned_by_self()) { |
| 510 | JfrMsg_lock->unlock(); |
| 511 | } |
| 512 | |
| 513 | if (JfrBuffer_lock->owned_by_self()) { |
| 514 | JfrBuffer_lock->unlock(); |
| 515 | } |
| 516 | |
| 517 | if (JfrStacktrace_lock->owned_by_self()) { |
| 518 | JfrStacktrace_lock->unlock(); |
| 519 | } |
| 520 | return true; |
| 521 | } |
| 522 | |
| 523 | static volatile int jfr_shutdown_lock = 0; |
| 524 | |
| 525 | static bool guard_reentrancy() { |
| 526 | return Atomic::cmpxchg(&jfr_shutdown_lock, 0, 1) == 0; |
| 527 | } |
| 528 | |
| 529 | class JavaThreadInVMAndNative : public StackObj { |
| 530 | private: |
| 531 | JavaThread* const _jt; |
| 532 | JavaThreadState _original_state; |
| 533 | public: |
| 534 | |
| 535 | JavaThreadInVMAndNative(Thread* t) : _jt(t->is_Java_thread() ? JavaThread::cast(t) : NULL__null), |
| 536 | _original_state(_thread_max_state) { |
| 537 | if (_jt != NULL__null) { |
| 538 | _original_state = _jt->thread_state(); |
| 539 | if (_original_state != _thread_in_vm) { |
| 540 | _jt->set_thread_state(_thread_in_vm); |
| 541 | } |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | ~JavaThreadInVMAndNative() { |
| 546 | if (_original_state != _thread_max_state) { |
| 547 | _jt->set_thread_state(_original_state); |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | void transition_to_native() { |
| 552 | if (_jt != NULL__null) { |
| 553 | assert(_jt->thread_state() == _thread_in_vm, "invariant")do { if (!(_jt->thread_state() == _thread_in_vm)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp" , 553, "assert(" "_jt->thread_state() == _thread_in_vm" ") failed" , "invariant"); ::breakpoint(); } } while (0); |
| 554 | _jt->set_thread_state(_thread_in_native); |
| 555 | } |
| 556 | } |
| 557 | }; |
| 558 | |
| 559 | static void post_events(bool exception_handler, Thread* thread) { |
| 560 | if (exception_handler) { |
| 561 | EventShutdown e; |
| 562 | e.set_reason("VM Error"); |
| 563 | e.commit(); |
| 564 | } else { |
| 565 | // OOM |
| 566 | LeakProfiler::emit_events(max_jlong, false, false); |
| 567 | } |
| 568 | EventDumpReason event; |
| 569 | event.set_reason(exception_handler ? "Crash" : "Out of Memory"); |
| 570 | event.set_recordingId(-1); |
| 571 | event.commit(); |
| 572 | } |
| 573 | |
| 574 | void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) { |
| 575 | if (!guard_reentrancy()) { |
| 576 | return; |
| 577 | } |
| 578 | Thread* thread = Thread::current_or_null_safe(); |
| 579 | if (thread == NULL__null) { |
| 580 | return; |
| 581 | } |
| 582 | // Ensure a JavaThread is _thread_in_vm when we make this call |
| 583 | JavaThreadInVMAndNative jtivm(thread); |
| 584 | if (!prepare_for_emergency_dump(thread)) { |
| 585 | return; |
| 586 | } |
| 587 | post_events(exception_handler, thread); |
| 588 | // if JavaThread, transition to _thread_in_native to issue a final flushpoint |
| 589 | NoHandleMark nhm; |
| 590 | jtivm.transition_to_native(); |
| 591 | const int messages = MSGBIT(MSG_VM_ERROR)(1<<(MSG_VM_ERROR)); |
| 592 | JfrRecorderService service; |
| 593 | service.rotate(messages); |
| 594 | } |