28 #include "jfr/leakprofiler/leakProfiler.hpp"
29 #include "jfr/recorder/repository/jfrEmergencyDump.hpp"
30 #include "jfr/recorder/service/jfrPostBox.hpp"
31 #include "jfr/recorder/service/jfrRecorderService.hpp"
32 #include "jfr/utilities/jfrTypes.hpp"
33 #include "logging/log.hpp"
34 #include "memory/resourceArea.hpp"
35 #include "runtime/atomic.hpp"
36 #include "runtime/handles.inline.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
43 static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr";
44 static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr";
45 static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr";
46 static const char chunk_file_jfr_ext[] = ".jfr";
47 static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS"
48
49 static fio_fd open_exclusivly(const char* path) {
50 return os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
51 }
52
53 static int file_sort(const char** const file1, const char** file2) {
54 assert(NULL != *file1 && NULL != *file2, "invariant");
55 int cmp = strncmp(*file1, *file2, iso8601_len);
56 if (0 == cmp) {
57 const char* const dot1 = strchr(*file1, '.');
58 assert(NULL != dot1, "invariant");
59 const char* const dot2 = strchr(*file2, '.');
60 assert(NULL != dot2, "invariant");
61 ptrdiff_t file1_len = dot1 - *file1;
62 ptrdiff_t file2_len = dot2 - *file2;
63 if (file1_len < file2_len) {
64 return -1;
65 }
66 if (file1_len > file2_len) {
67 return 1;
195 const char* const entry_path = filter(dentry->d_name);
196 if (NULL != entry_path) {
197 _files->append(entry_path);
198 }
199 }
200 os::closedir(dirp);
201 if (_files->length() > 1) {
202 _files->sort(file_sort);
203 }
204 }
205 }
206
207 bool RepositoryIterator::has_next() const {
208 return (_files != NULL && _iterator < _files->length());
209 }
210
211 const char* const RepositoryIterator::next() const {
212 return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++));
213 }
214
215 static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& iterator) {
216 assert(emergency_fd != invalid_fd, "invariant");
217 const size_t size_of_file_copy_block = 1 * M; // 1 mb
218 jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block);
219 if (file_copy_block == NULL) {
220 return;
221 }
222 while (iterator.has_next()) {
223 fio_fd current_fd = invalid_fd;
224 const char* const fqn = iterator.next();
225 if (fqn != NULL) {
226 current_fd = open_exclusivly(fqn);
227 if (current_fd != invalid_fd) {
228 const int64_t current_filesize = file_size(current_fd);
229 assert(current_filesize > 0, "invariant");
230 int64_t bytes_read = 0;
231 int64_t bytes_written = 0;
232 while (bytes_read < current_filesize) {
233 const ssize_t read_result = os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read);
234 if (-1 == read_result) {
235 log_info(jfr)( // For user, should not be "jfr, system"
236 "Unable to recover JFR data");
237 break;
238 }
239 bytes_read += (int64_t)read_result;
240 assert(bytes_read - bytes_written <= (int64_t)size_of_file_copy_block, "invariant");
241 bytes_written += (int64_t)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written);
242 assert(bytes_read == bytes_written, "invariant");
243 }
244 os::close(current_fd);
245 }
246 }
247 }
248 }
249
250 static const char* create_emergency_dump_path() {
251 char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
252 if (NULL == buffer) {
253 return NULL;
254 }
255 const char* const cwd = os::get_current_directory(buffer, JVM_MAXPATHLEN);
256 if (NULL == cwd) {
257 return NULL;
258 }
259 size_t pos = strlen(cwd);
260 const int fsep_len = jio_snprintf(&buffer[pos], JVM_MAXPATHLEN - pos, "%s", os::file_separator());
261 const char* filename_fmt = NULL;
262 // fetch specific error cause
263 switch (JfrJavaSupport::cause()) {
264 case JfrJavaSupport::OUT_OF_MEMORY:
265 filename_fmt = vm_oom_filename_fmt;
266 break;
267 case JfrJavaSupport::STACK_OVERFLOW:
268 filename_fmt = vm_soe_filename_fmt;
269 break;
270 default:
271 filename_fmt = vm_error_filename_fmt;
272 }
273 char* emergency_dump_path = NULL;
274 pos += fsep_len;
275 if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], JVM_MAXPATHLEN - pos)) {
276 const size_t emergency_filename_length = strlen(buffer);
277 emergency_dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, emergency_filename_length + 1);
278 if (NULL == emergency_dump_path) {
279 return NULL;
280 }
281 strncpy(emergency_dump_path, buffer, emergency_filename_length + 1);
282 }
283 if (emergency_dump_path != NULL) {
284 log_info(jfr)( // For user, should not be "jfr, system"
285 "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path);
286 }
287 return emergency_dump_path;
288 }
289
290 // Caller needs ResourceMark
291 static const char* create_emergency_chunk_path(const char* repository_path) {
292 assert(repository_path != NULL, "invariant");
293 const size_t repository_path_len = strlen(repository_path);
294 // date time
295 char date_time_buffer[32] = { 0 };
296 date_time(date_time_buffer, sizeof(date_time_buffer));
297 size_t date_time_len = strlen(date_time_buffer);
298 size_t chunkname_max_len = repository_path_len // repository_base_path
299 + 1 // "/"
300 + date_time_len // date_time
301 + strlen(chunk_file_jfr_ext) // .jfr
302 + 1;
303 char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len);
304 if (chunk_path == NULL) {
305 return NULL;
306 }
307 // append the individual substrings
308 jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
309 return chunk_path;
310 }
311
312 static fio_fd emergency_dump_file_descriptor() {
313 ResourceMark rm;
314 const char* const emergency_dump_path = create_emergency_dump_path();
315 return emergency_dump_path != NULL ? open_exclusivly(emergency_dump_path) : invalid_fd;
316 }
317
318 const char* JfrEmergencyDump::build_dump_path(const char* repository_path) {
319 return repository_path == NULL ? create_emergency_dump_path() : create_emergency_chunk_path(repository_path);
320 }
321
322 void JfrEmergencyDump::on_vm_error(const char* repository_path) {
323 assert(repository_path != NULL, "invariant");
324 ResourceMark rm;
325 const fio_fd emergency_fd = emergency_dump_file_descriptor();
326 if (emergency_fd != invalid_fd) {
327 RepositoryIterator iterator(repository_path, strlen(repository_path));
328 write_emergency_file(emergency_fd, iterator);
329 os::close(emergency_fd);
330 }
331 }
332
333 /*
334 * We are just about to exit the VM, so we will be very aggressive
335 * at this point in order to increase overall success of dumping jfr data.
336 *
337 * If we end up deadlocking in the attempt of dumping out jfr data,
338 * we rely on the WatcherThread task "is_error_reported()",
339 * to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump).
340 * This "safety net" somewhat explains the aggressiveness in this attempt.
341 *
342 */
343 static bool prepare_for_emergency_dump(Thread* thread) {
344 assert(thread != NULL, "invariant");
345
346 if (thread->is_Watcher_thread()) {
347 // need WatcherThread as a safeguard against potential deadlocks
348 return false;
349 }
|
28 #include "jfr/leakprofiler/leakProfiler.hpp"
29 #include "jfr/recorder/repository/jfrEmergencyDump.hpp"
30 #include "jfr/recorder/service/jfrPostBox.hpp"
31 #include "jfr/recorder/service/jfrRecorderService.hpp"
32 #include "jfr/utilities/jfrTypes.hpp"
33 #include "logging/log.hpp"
34 #include "memory/resourceArea.hpp"
35 #include "runtime/atomic.hpp"
36 #include "runtime/handles.inline.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
43 static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr";
44 static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr";
45 static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr";
46 static const char chunk_file_jfr_ext[] = ".jfr";
47 static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS"
48 static fio_fd emergency_fd = invalid_fd;
49
50 static fio_fd open_exclusivly(const char* path) {
51 return os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
52 }
53
54 static int file_sort(const char** const file1, const char** file2) {
55 assert(NULL != *file1 && NULL != *file2, "invariant");
56 int cmp = strncmp(*file1, *file2, iso8601_len);
57 if (0 == cmp) {
58 const char* const dot1 = strchr(*file1, '.');
59 assert(NULL != dot1, "invariant");
60 const char* const dot2 = strchr(*file2, '.');
61 assert(NULL != dot2, "invariant");
62 ptrdiff_t file1_len = dot1 - *file1;
63 ptrdiff_t file2_len = dot2 - *file2;
64 if (file1_len < file2_len) {
65 return -1;
66 }
67 if (file1_len > file2_len) {
68 return 1;
196 const char* const entry_path = filter(dentry->d_name);
197 if (NULL != entry_path) {
198 _files->append(entry_path);
199 }
200 }
201 os::closedir(dirp);
202 if (_files->length() > 1) {
203 _files->sort(file_sort);
204 }
205 }
206 }
207
208 bool RepositoryIterator::has_next() const {
209 return (_files != NULL && _iterator < _files->length());
210 }
211
212 const char* const RepositoryIterator::next() const {
213 return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++));
214 }
215
216 static void write_emergency_file(const RepositoryIterator& iterator) {
217 assert(emergency_fd != invalid_fd, "invariant");
218 const size_t size_of_file_copy_block = 1 * M; // 1 mb
219 jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block);
220 if (file_copy_block == NULL) {
221 return;
222 }
223 while (iterator.has_next()) {
224 fio_fd current_fd = invalid_fd;
225 const char* const fqn = iterator.next();
226 if (fqn != NULL) {
227 current_fd = open_exclusivly(fqn);
228 if (current_fd != invalid_fd) {
229 const int64_t current_filesize = file_size(current_fd);
230 assert(current_filesize > 0, "invariant");
231 int64_t bytes_read = 0;
232 int64_t bytes_written = 0;
233 while (bytes_read < current_filesize) {
234 const ssize_t read_result = os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read);
235 if (-1 == read_result) {
236 log_info(jfr)( // For user, should not be "jfr, system"
237 "Unable to recover JFR data");
238 break;
239 }
240 bytes_read += (int64_t)read_result;
241 assert(bytes_read - bytes_written <= (int64_t)size_of_file_copy_block, "invariant");
242 bytes_written += (int64_t)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written);
243 assert(bytes_read == bytes_written, "invariant");
244 }
245 os::close(current_fd);
246 }
247 }
248 }
249 }
250
251 // "emergency_dump_path" have to be allocated JVM_MAXPATHLEN or larger.
252 static void create_emergency_dump_path(char* emergency_dump_path) {
253 char buffer[JVM_MAXPATHLEN];
254 emergency_dump_path[0] = '\0';
255 const char* const cwd = os::get_current_directory(buffer, JVM_MAXPATHLEN);
256 if (NULL == cwd) {
257 return;
258 }
259 size_t pos = strlen(cwd);
260 const int fsep_len = jio_snprintf(&buffer[pos], JVM_MAXPATHLEN - pos, "%s", os::file_separator());
261 const char* filename_fmt = NULL;
262 // fetch specific error cause
263 switch (JfrJavaSupport::cause()) {
264 case JfrJavaSupport::OUT_OF_MEMORY:
265 filename_fmt = vm_oom_filename_fmt;
266 break;
267 case JfrJavaSupport::STACK_OVERFLOW:
268 filename_fmt = vm_soe_filename_fmt;
269 break;
270 default:
271 filename_fmt = vm_error_filename_fmt;
272 }
273 pos += fsep_len;
274 if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], JVM_MAXPATHLEN - pos)) {
275 strncpy(emergency_dump_path, buffer, JVM_MAXPATHLEN);
276 }
277 if (emergency_dump_path[0] != '\0') {
278 log_info(jfr)( // For user, should not be "jfr, system"
279 "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path);
280 }
281 }
282
283 // Caller needs ResourceMark
284 static const char* create_emergency_chunk_path(const char* repository_path) {
285 assert(repository_path != NULL, "invariant");
286 const size_t repository_path_len = strlen(repository_path);
287 // date time
288 char date_time_buffer[32] = { 0 };
289 date_time(date_time_buffer, sizeof(date_time_buffer));
290 size_t date_time_len = strlen(date_time_buffer);
291 size_t chunkname_max_len = repository_path_len // repository_base_path
292 + 1 // "/"
293 + date_time_len // date_time
294 + strlen(chunk_file_jfr_ext) // .jfr
295 + 1;
296 char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len);
297 if (chunk_path == NULL) {
298 return NULL;
299 }
300 // append the individual substrings
301 jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
302 return chunk_path;
303 }
304
305 void JfrEmergencyDump::setup_emergency_dump_file_descriptor() {
306 assert(emergency_fd == invalid_fd, "invariant");
307 char emergency_dump_path[JVM_MAXPATHLEN];
308 create_emergency_dump_path(emergency_dump_path);
309 emergency_fd = (emergency_dump_path[0] != '\0') ? open_exclusivly(emergency_dump_path) : invalid_fd;
310 }
311
312 // Caller needs ResourceMark
313 const char* JfrEmergencyDump::build_dump_path(const char* repository_path) {
314 if (repository_path == NULL) {
315 char* dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
316 if (dump_path == NULL) {
317 return NULL;
318 }
319 create_emergency_dump_path(dump_path);
320 return dump_path;
321 } else {
322 return create_emergency_chunk_path(repository_path);
323 }
324 }
325
326 void JfrEmergencyDump::on_vm_error(const char* repository_path) {
327 assert(repository_path != NULL, "invariant");
328 ResourceMark rm;
329 if (emergency_fd != invalid_fd) {
330 RepositoryIterator iterator(repository_path, strlen(repository_path));
331 write_emergency_file(iterator);
332 os::close(emergency_fd);
333 emergency_fd = invalid_fd;
334 }
335 }
336
337 /*
338 * We are just about to exit the VM, so we will be very aggressive
339 * at this point in order to increase overall success of dumping jfr data.
340 *
341 * If we end up deadlocking in the attempt of dumping out jfr data,
342 * we rely on the WatcherThread task "is_error_reported()",
343 * to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump).
344 * This "safety net" somewhat explains the aggressiveness in this attempt.
345 *
346 */
347 static bool prepare_for_emergency_dump(Thread* thread) {
348 assert(thread != NULL, "invariant");
349
350 if (thread->is_Watcher_thread()) {
351 // need WatcherThread as a safeguard against potential deadlocks
352 return false;
353 }
|