Mon, 12 Aug 2019 18:30:40 +0300
8223147: JFR Backport
8199712: Flight Recorder
8203346: JFR: Inconsistent signature of jfr_add_string_constant
8195817: JFR.stop should require name of recording
8195818: JFR.start should increase autogenerated name by one
8195819: Remove recording=x from jcmd JFR.check output
8203921: JFR thread sampling is missing fixes from JDK-8194552
8203929: Limit amount of data for JFR.dump
8203664: JFR start failure after AppCDS archive created with JFR StartFlightRecording
8003209: JFR events for network utilization
8207392: [PPC64] Implement JFR profiling
8202835: jfr/event/os/TestSystemProcess.java fails on missing events
Summary: Backport JFR from JDK11. Initial integration
Reviewed-by: neugens
1 /*
2 * Copyright (c) 2011, 2018, 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 */
25 #include "precompiled.hpp"
26 #include "jfr/jfr.hpp"
27 #include "jfr/jni/jfrJavaSupport.hpp"
28 #include "jfr/recorder/jfrRecorder.hpp"
29 #include "jfr/recorder/repository/jfrChunkState.hpp"
30 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
31 #include "jfr/recorder/repository/jfrRepository.hpp"
32 #include "jfr/recorder/service/jfrPostBox.hpp"
33 #include "memory/resourceArea.hpp"
34 #include "runtime/mutex.hpp"
35 #include "runtime/arguments.hpp"
36 #include "runtime/os.hpp"
37 #include "runtime/thread.inline.hpp"
39 static JfrRepository* _instance = NULL;
41 JfrRepository& JfrRepository::instance() {
42 return *_instance;
43 }
45 static JfrChunkWriter* _chunkwriter = NULL;
47 static bool initialize_chunkwriter() {
48 assert(_chunkwriter == NULL, "invariant");
49 _chunkwriter = new JfrChunkWriter();
50 return _chunkwriter != NULL && _chunkwriter->initialize();
51 }
53 JfrChunkWriter& JfrRepository::chunkwriter() {
54 return *_chunkwriter;
55 }
57 JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
59 bool JfrRepository::initialize() {
60 return initialize_chunkwriter();
61 }
63 JfrRepository::~JfrRepository() {
64 if (_path != NULL) {
65 JfrCHeapObj::free(_path, strlen(_path) + 1);
66 _path = NULL;
67 }
69 if (_chunkwriter != NULL) {
70 delete _chunkwriter;
71 _chunkwriter = NULL;
72 }
73 }
75 JfrRepository* JfrRepository::create(JfrPostBox& post_box) {
76 assert(_instance == NULL, "invariant");
77 _instance = new JfrRepository(post_box);
78 return _instance;
79 }
81 void JfrRepository::destroy() {
82 assert(_instance != NULL, "invariant");
83 delete _instance;
84 _instance = NULL;
85 }
87 static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr";
88 static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr";
89 static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr";
90 static const char chunk_file_jfr_ext[] = ".jfr";
91 static const size_t iso8601_len = 19; // "YYYY-MM-DDTHH:MM:SS"
93 static fio_fd open_exclusivly(const char* path) {
94 return os::open(path, O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
95 }
97 static fio_fd open_existing(const char* path) {
98 return os::open(path, O_RDWR, S_IREAD | S_IWRITE);
99 }
101 static int file_sort(const char** const file1, const char** file2) {
102 assert(NULL != *file1 && NULL != *file2, "invariant");
103 int cmp = strncmp(*file1, *file2, iso8601_len);
104 if (0 == cmp) {
105 const char* const dot1 = strchr(*file1, '.');
106 assert(NULL != dot1, "invariant");
107 const char* const dot2 = strchr(*file2, '.');
108 assert(NULL != dot2, "invariant");
109 ptrdiff_t file1_len = dot1 - *file1;
110 ptrdiff_t file2_len = dot2 - *file2;
111 if (file1_len < file2_len) {
112 return -1;
113 }
114 if (file1_len > file2_len) {
115 return 1;
116 }
117 assert(file1_len == file2_len, "invariant");
118 cmp = strncmp(*file1, *file2, file1_len);
119 }
120 assert(cmp != 0, "invariant");
121 return cmp;
122 }
124 static void iso8601_to_date_time(char* iso8601_str) {
125 assert(iso8601_str != NULL, "invariant");
126 assert(strlen(iso8601_str) == iso8601_len, "invariant");
127 // "YYYY-MM-DDTHH:MM:SS"
128 for (size_t i = 0; i < iso8601_len; ++i) {
129 switch(iso8601_str[i]) {
130 case 'T' :
131 case '-' :
132 case ':' :
133 iso8601_str[i] = '_';
134 break;
135 }
136 }
137 // "YYYY_MM_DD_HH_MM_SS"
138 }
140 static void date_time(char* buffer, size_t buffer_len) {
141 assert(buffer != NULL, "invariant");
142 assert(buffer_len >= iso8601_len, "buffer too small");
143 os::iso8601_time(buffer, buffer_len);
144 assert(strlen(buffer) >= iso8601_len + 1, "invariant");
145 // "YYYY-MM-DDTHH:MM:SS"
146 buffer[iso8601_len] = '\0';
147 iso8601_to_date_time(buffer);
148 }
150 static jlong file_size(fio_fd fd) {
151 assert(fd != invalid_fd, "invariant");
152 const jlong current_offset = os::current_file_offset(fd);
153 const jlong size = os::lseek(fd, 0, SEEK_END);
154 os::seek_to_file_offset(fd, current_offset);
155 return size;
156 }
158 class RepositoryIterator : public StackObj {
159 private:
160 const char* const _repo;
161 const size_t _repository_len;
162 GrowableArray<const char*>* _files;
163 const char* const fully_qualified(const char* entry) const;
164 mutable int _iterator;
166 public:
167 RepositoryIterator(const char* repository, size_t repository_len);
168 ~RepositoryIterator() {}
169 debug_only(void print_repository_files() const;)
170 const char* const filter(const char* entry) const;
171 bool has_next() const;
172 const char* const next() const;
173 };
175 const char* const RepositoryIterator::fully_qualified(const char* entry) const {
176 assert(NULL != entry, "invariant");
177 char* file_path_entry = NULL;
178 // only use files that have content, not placeholders
179 const char* const file_separator = os::file_separator();
180 if (NULL != file_separator) {
181 const size_t entry_len = strlen(entry);
182 const size_t file_separator_length = strlen(file_separator);
183 const size_t file_path_entry_length = _repository_len + file_separator_length + entry_len;
184 file_path_entry = NEW_RESOURCE_ARRAY_RETURN_NULL(char, file_path_entry_length + 1);
185 if (NULL == file_path_entry) {
186 return NULL;
187 }
188 int position = 0;
189 position += jio_snprintf(&file_path_entry[position], _repository_len + 1, "%s", _repo);
190 position += jio_snprintf(&file_path_entry[position], file_separator_length + 1, "%s", os::file_separator());
191 position += jio_snprintf(&file_path_entry[position], entry_len + 1, "%s", entry);
192 file_path_entry[position] = '\0';
193 assert((size_t)position == file_path_entry_length, "invariant");
194 assert(strlen(file_path_entry) == (size_t)position, "invariant");
195 }
196 return file_path_entry;
197 }
199 const char* const RepositoryIterator::filter(const char* entry) const {
200 if (entry == NULL) {
201 return NULL;
202 }
203 const size_t entry_len = strlen(entry);
204 if (entry_len <= 2) {
205 // for "." and ".."
206 return NULL;
207 }
208 char* entry_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, entry_len + 1);
209 if (entry_name == NULL) {
210 return NULL;
211 }
212 strncpy(entry_name, entry, entry_len);
213 entry_name[entry_len] = '\0';
214 const char* const fully_qualified_path_entry = fully_qualified(entry_name);
215 if (NULL == fully_qualified_path_entry) {
216 return NULL;
217 }
218 const fio_fd entry_fd = open_existing(fully_qualified_path_entry);
219 if (invalid_fd == entry_fd) {
220 return NULL;
221 }
222 const jlong entry_size = file_size(entry_fd);
223 os::close(entry_fd);
224 if (0 == entry_size) {
225 return NULL;
226 }
227 return entry_name;
228 }
230 RepositoryIterator::RepositoryIterator(const char* repository, size_t repository_len) :
231 _repo(repository),
232 _repository_len(repository_len),
233 _files(NULL),
234 _iterator(0) {
235 if (NULL != _repo) {
236 assert(strlen(_repo) == _repository_len, "invariant");
237 _files = new GrowableArray<const char*>(10);
238 DIR* dirp = os::opendir(_repo);
239 if (dirp == NULL) {
240 if (true) tty->print_cr("Unable to open repository %s", _repo);
241 return;
242 }
243 struct dirent* dentry;
244 while ((dentry = os::readdir(dirp)) != NULL) {
245 const char* const entry_path = filter(dentry->d_name);
246 if (NULL != entry_path) {
247 _files->append(entry_path);
248 }
249 }
250 os::closedir(dirp);
251 if (_files->length() > 1) {
252 _files->sort(file_sort);
253 }
254 }
255 }
257 #ifdef ASSERT
258 void RepositoryIterator::print_repository_files() const {
259 while (has_next()) {
260 if (true) tty->print_cr( "%s", next());
261 }
262 }
263 #endif
264 bool RepositoryIterator::has_next() const {
265 return (_files != NULL && _iterator < _files->length());
266 }
268 const char* const RepositoryIterator::next() const {
269 return _iterator >= _files->length() ? NULL : fully_qualified(_files->at(_iterator++));
270 }
272 static void write_emergency_file(fio_fd emergency_fd, const RepositoryIterator& iterator) {
273 assert(emergency_fd != invalid_fd, "invariant");
274 const size_t size_of_file_copy_block = 1 * M; // 1 mb
275 jbyte* const file_copy_block = NEW_RESOURCE_ARRAY_RETURN_NULL(jbyte, size_of_file_copy_block);
276 if (file_copy_block == NULL) {
277 return;
278 }
279 jlong bytes_written_total = 0;
280 while (iterator.has_next()) {
281 fio_fd current_fd = invalid_fd;
282 const char* const fqn = iterator.next();
283 if (fqn != NULL) {
284 current_fd = open_existing(fqn);
285 if (current_fd != invalid_fd) {
286 const jlong current_filesize = file_size(current_fd);
287 assert(current_filesize > 0, "invariant");
288 jlong bytes_read = 0;
289 jlong bytes_written = 0;
290 while (bytes_read < current_filesize) {
291 bytes_read += (jlong)os::read_at(current_fd, file_copy_block, size_of_file_copy_block, bytes_read);
292 assert(bytes_read - bytes_written <= (jlong)size_of_file_copy_block, "invariant");
293 bytes_written += (jlong)os::write(emergency_fd, file_copy_block, bytes_read - bytes_written);
294 assert(bytes_read == bytes_written, "invariant");
295 }
296 os::close(current_fd);
297 bytes_written_total += bytes_written;
298 }
299 }
300 }
301 }
303 static const char* create_emergency_dump_path() {
304 assert(JfrStream_lock->owned_by_self(), "invariant");
305 char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN);
306 if (NULL == buffer) {
307 return NULL;
308 }
309 const char* const cwd = os::get_current_directory(buffer, O_BUFLEN);
310 if (NULL == cwd) {
311 return NULL;
312 }
313 size_t pos = strlen(cwd);
314 const int fsep_len = jio_snprintf(&buffer[pos], O_BUFLEN - pos, "%s", os::file_separator());
315 const char* filename_fmt = NULL;
316 // fetch specific error cause
317 switch (JfrJavaSupport::cause()) {
318 case JfrJavaSupport::OUT_OF_MEMORY:
319 filename_fmt = vm_oom_filename_fmt;
320 break;
321 case JfrJavaSupport::STACK_OVERFLOW:
322 filename_fmt = vm_soe_filename_fmt;
323 break;
324 default:
325 filename_fmt = vm_error_filename_fmt;
326 }
327 char* emergency_dump_path = NULL;
328 pos += fsep_len;
329 if (Arguments::copy_expand_pid(filename_fmt, strlen(filename_fmt), &buffer[pos], O_BUFLEN - pos)) {
330 const size_t emergency_filename_length = strlen(buffer);
331 emergency_dump_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, emergency_filename_length + 1);
332 if (NULL == emergency_dump_path) {
333 return NULL;
334 }
335 strncpy(emergency_dump_path, buffer, emergency_filename_length);
336 emergency_dump_path[emergency_filename_length] = '\0';
337 }
338 return emergency_dump_path;
339 }
341 // Caller needs ResourceMark
342 static const char* create_emergency_chunk_path(const char* repository_base, size_t repository_len) {
343 assert(repository_base != NULL, "invariant");
344 assert(JfrStream_lock->owned_by_self(), "invariant");
345 // date time
346 char date_time_buffer[32] = {0};
347 date_time(date_time_buffer, sizeof(date_time_buffer));
348 size_t date_time_len = strlen(date_time_buffer);
349 size_t chunkname_max_len = repository_len // repository_base
350 + 1 // "/"
351 + date_time_len // date_time
352 + strlen(chunk_file_jfr_ext) // .jfr
353 + 1;
354 char* chunk_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, chunkname_max_len);
355 if (chunk_path == NULL) {
356 return NULL;
357 }
358 // append the individual substrings
359 jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_base, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
360 return chunk_path;
361 }
363 static fio_fd emergency_dump_file() {
364 assert(JfrStream_lock->owned_by_self(), "invariant");
365 ResourceMark rm;
366 const char* const emergency_dump_path = create_emergency_dump_path();
367 if (emergency_dump_path == NULL) {
368 return invalid_fd;
369 }
370 const fio_fd fd = open_exclusivly(emergency_dump_path);
371 if (fd != invalid_fd) {
372 if (LogJFR) tty->print_cr( // For user, should not be "jfr, system"
373 "Attempting to recover JFR data, emergency jfr file: %s", emergency_dump_path);
374 }
375 return fd;
376 }
378 static const char* emergency_path(const char* repository, size_t repository_len) {
379 return repository == NULL ? create_emergency_dump_path() : create_emergency_chunk_path(repository, repository_len);
380 }
382 void JfrRepository::on_vm_error() {
383 assert(!JfrStream_lock->owned_by_self(), "invariant");
384 const char* path = _path;
385 if (path == NULL) {
386 // completed already
387 return;
388 }
389 ResourceMark rm;
390 MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
391 const fio_fd emergency_fd = emergency_dump_file();
392 if (emergency_fd != invalid_fd) {
393 RepositoryIterator iterator(path, strlen(path));
394 write_emergency_file(emergency_fd, iterator);
395 os::close(emergency_fd);
396 }
397 }
399 bool JfrRepository::set_path(const char* path) {
400 assert(path != NULL, "trying to set the repository path with a NULL string!");
401 if (_path != NULL) {
402 // delete existing
403 JfrCHeapObj::free(_path, strlen(_path) + 1);
404 }
405 const size_t path_len = strlen(path);
406 _path = JfrCHeapObj::new_array<char>(path_len + 1);
407 if (_path == NULL) {
408 return false;
409 }
410 strncpy(_path, path, path_len);
411 _path[path_len] = '\0';
412 return true;
413 }
415 void JfrRepository::set_chunk_path(const char* path) {
416 assert(JfrStream_lock->owned_by_self(), "invariant");
417 chunkwriter().set_chunk_path(path);
418 }
420 void JfrRepository::notify_on_new_chunk_path() {
421 if (Jfr::is_recording()) {
422 instance()._post_box.post(MSG_ROTATE);
423 }
424 }
426 /**
427 * Sets the file where data should be written.
428 *
429 * Recording Previous Current Action
430 * ==============================================
431 * true null null Ignore, keep recording in-memory
432 * true null file1 Start disk recording
433 * true file null Copy out metadata to disk and continue in-memory recording
434 * true file1 file2 Copy out metadata and start with new File (file2)
435 * false * null Ignore, but start recording to memory
436 * false * file Ignore, but start recording to disk
437 */
438 void JfrRepository::set_chunk_path(jstring path, JavaThread* jt) {
439 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
440 ResourceMark rm(jt);
441 const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
442 {
443 MutexLockerEx stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
444 if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
445 // new output is NULL and current output is NULL
446 return;
447 }
448 instance().set_chunk_path(canonical_chunk_path);
449 }
450 notify_on_new_chunk_path();
451 }
453 void JfrRepository::set_path(jstring location, JavaThread* jt) {
454 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
455 ResourceMark rm(jt);
456 const char* const path = JfrJavaSupport::c_str(location, jt);
457 if (path != NULL) {
458 instance().set_path(path);
459 }
460 }
462 bool JfrRepository::open_chunk(bool vm_error /* false */) {
463 assert(JfrStream_lock->owned_by_self(), "invariant");
464 if (vm_error) {
465 ResourceMark rm;
466 const char* repository_path = _path;
467 const size_t repository_path_len = repository_path != NULL ? strlen(repository_path) : 0;
468 const char* const path = emergency_path(repository_path, repository_path_len);
469 _chunkwriter->set_chunk_path(path);
470 }
471 return _chunkwriter->open();
472 }
474 size_t JfrRepository::close_chunk(jlong metadata_offset) {
475 return _chunkwriter->close(metadata_offset);
476 }