1 /*
2 * Copyright (c) 2023, 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 "cds/aotMetaspace.hpp"
26 #include "cds/cdsConfig.hpp"
27 #include "cds/cdsEndTrainingUpcall.hpp"
28 #include "compiler/methodMatcher.hpp"
29 #include "runtime/globals_extension.hpp"
30 #include "runtime/interfaceSupport.inline.hpp"
31 #include "runtime/runtimeUpcalls.hpp"
32
33 uint volatile CDSEndTrainingUpcall::_count = 0;
34 uint CDSEndTrainingUpcall::_limit = 1;
35 int volatile CDSEndTrainingUpcall::_triggered = 0;
36 BasicMatcher* CDSEndTrainingUpcall::_matcher = nullptr;
37
38 bool cdsEndTrainingUpcall_register_upcalls()
39 {
40 if (!CDSConfig::is_dumping_preimage_static_archive_with_triggers()) {
41 return true;
42 }
43 return CDSEndTrainingUpcall::register_upcalls();
44 }
45
46 bool CDSEndTrainingUpcall::register_upcalls()
47 {
48 if (!FLAG_IS_DEFAULT(AOTEndTrainingOnMethodEntry)) {
49 if (CDSEndTrainingUpcall::parse_vm_command(AOTEndTrainingOnMethodEntry)) {
50 return RuntimeUpcalls::register_upcall(
51 RuntimeUpcallType::onMethodEntry,
52 "end_training_check",
53 CDSEndTrainingUpcall::end_training_check,
54 CDSEndTrainingUpcall::filter_method_callback
55 );
56 }
57 return false;
58 }
59 return true;
60 }
61
62 JRT_ENTRY(void, CDSEndTrainingUpcall::end_training_check(JavaThread* current))
63 {
64 if (_triggered == 0) {
65 AtomicAccess::inc(&_count);
66 if(_count >= _limit) {
67 CDSEndTrainingUpcall::end_training(current);
68 }
69 }
70 }
71 JRT_END
72
73 bool CDSEndTrainingUpcall::end_training(JavaThread* current)
74 {
75 if (_triggered == 0) {
76 if (AtomicAccess::cmpxchg(&_triggered, 0, 1) == 0) {
77 AOTMetaspace::dump_static_archive(current);
78 assert(!current->has_pending_exception(), "Unexpected exception");
79 return true;
80 }
81 }
82 return false;
83 }
84
85 bool CDSEndTrainingUpcall::filter_method_callback(MethodDetails& method_details)
86 {
87 if (_matcher != nullptr) {
88 return _matcher->match(method_details);
89 }
90 return false;
91 }
92
93 bool CDSEndTrainingUpcall::parse_vm_command(ccstrlist command)
94 {
95 assert(command != nullptr, "sanity");
96 ResourceMark rm;
97 const char* error_msg = nullptr;
98 char* copy = os::strdup(command, mtInternal);
99 char* line = copy;
100 char* method_pattern;
101 int num_patterns = 0;
102 bool error = false;
103 const char* seperator_str = ",";
104 const char* count_str = "count=";
105 const size_t count_str_len = strlen(count_str);
106 do {
107 if (line[0] == '\0') {
108 break;
109 }
110 method_pattern = strtok_r(line, seperator_str, &line);
111 if (method_pattern != nullptr) {
112 // if method pattern starts with count=, then parse the count
113 if (strncmp(method_pattern, count_str, count_str_len) == 0) {
114 int number = atoi(method_pattern + count_str_len);
115 if (number > 0) {
116 CDSEndTrainingUpcall::set_limit((uint)number);
117 continue;
118 }
119 error_msg = "count must be a valid integer > 0";
120 } else {
121 BasicMatcher* matcher = BasicMatcher::parse_method_pattern(method_pattern, error_msg, false);
122 if (matcher != nullptr) {
123 if (_matcher != nullptr) {
124 matcher->set_next(_matcher);
125 }
126 _matcher = matcher;
127 num_patterns++;
128 continue;
129 }
130 }
131 }
132 ttyLocker ttyl;
133 tty->print_cr("An error occurred during parsing AOTEndTrainingOnMethodEntry");
134 if (error_msg != nullptr) {
135 tty->print_cr("Error: %s", error_msg);
136 }
137 tty->print_cr("Line: '%s'", command);
138 error = true;
139 } while (!error && method_pattern != nullptr && line != nullptr);
140 os::free(copy);
141 if (num_patterns == 0) {
142 tty->print_cr("Error: No method patterns found in AOTEndTrainingOnMethodEntry");
143 error = true;
144 }
145 return !error;
146 }