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 | } |