1 /*
2 * Copyright (c) 1997, 2024, 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 "runtime/interfaceSupport.inline.hpp"
26 #include "runtime/methodDetails.hpp"
27 #include "runtime/runtimeUpcalls.hpp"
28
29 GrowableArray<RuntimeUpcallInfo*>* RuntimeUpcalls::_upcalls[RuntimeUpcallType::numTypes];
30 RuntimeUpcalls::State RuntimeUpcalls::_state = RuntimeUpcalls::Uninitialized;
31
32 bool runtimeUpcalls_open_registration() {
33 return RuntimeUpcalls::open_upcall_registration();
34 }
35
36 bool RuntimeUpcalls::open_upcall_registration() {
37 assert(_state == Uninitialized, "upcalls are already open");
38 for (int i = 0; i < RuntimeUpcallType::numTypes; i++) {
39 RuntimeUpcalls::_upcalls[i] = nullptr;
40 }
41 _state = Open;
42 return true;
43 }
44
45 void runtimeUpcalls_close_registration() {
46 RuntimeUpcalls::close_upcall_registration();
47 }
48
49 void RuntimeUpcalls::close_upcall_registration() {
50 assert(_state == Open, "upcalls are not open");
51 _state = Closed;
52 }
53
54 void RuntimeUpcalls::mark_for_upcalls(RuntimeUpcallType upcall_type, const methodHandle& method) {
55 if (_upcalls[upcall_type] != nullptr) {
56 MethodDetails method_details(method);
57 for(RuntimeUpcallInfo* info : *(_upcalls[upcall_type])) {
58 if(info->includes(method_details)) {
59 switch(upcall_type) {
60 case onMethodEntry: method->set_has_upcall_on_method_entry(true); break;
61 case onMethodExit: method->set_has_upcall_on_method_exit(true); break;
62 default: ShouldNotReachHere();
63 }
64 break;
65 }
66 }
67 }
68 }
69
70 bool RuntimeUpcalls::register_upcall(RuntimeUpcallType upcall_type, RuntimeUpcallInfo* info) {
71 assert(upcall_type != onMethodExit, "Upcalls on method exit are not supported yet");
72 assert(info != nullptr, "upcall info is null");
73 if (_upcalls[upcall_type] == nullptr) {
74 _upcalls[upcall_type] = new (mtServiceability) GrowableArray<RuntimeUpcallInfo*>(1, mtServiceability);
75 }
76 info->set_index(_upcalls[upcall_type]->length());
77 _upcalls[upcall_type]->append(info);
78 return true;
79 }
80
81 int RuntimeUpcalls::get_num_upcalls(RuntimeUpcallType upcall_type) {
82 return (_upcalls[upcall_type] == nullptr) ? 0 : _upcalls[upcall_type]->length();
83 }
84
85 void RuntimeUpcalls::upcall_redirect(RuntimeUpcallType upcall_type, JavaThread* current, Method* method) {
86 MethodDetails method_details(method);
87
88 // This redirection occurs when there are more than one upcalls setup. Currently each method is marked
89 // to indicate either none, entry and/or exit upcalls (two bits total); then we have to iterate over
90 // all upcalls and test the method details to determine which upcalls to call. This is not optimal.
91 // One possible optimization is to use more bits to support more upcalls. The method flags currently use 18
92 // out of 32 bits, so there are still 14 bits available for use. We could set a limit of say 4-8 entry/exit
93 // upcalls combined, leaving 10-6 bits for other uses. This still requires a redirect here to determine
94 // which upcalls to call, but it would be more efficient than the current implementation as we'd avoid the
95 // method matching and simply map bits to indexes.
96
97 RuntimeUpcallInfo* upcall = get_first_upcall(upcall_type, method_details);
98 while (upcall != nullptr) {
99 upcall->upcall()(current);
100 upcall = get_next_upcall(upcall_type, method_details, upcall);
101 }
102 }
103
104 JRT_BLOCK_ENTRY(void, RuntimeUpcalls::on_method_entry_upcall_redirect(JavaThread* current, Method* method)) {
105 RuntimeUpcalls::upcall_redirect(onMethodEntry, current, method);
106 }
107 JRT_END
108
109 JRT_BLOCK_ENTRY(void, RuntimeUpcalls::on_method_exit_upcall_redirect(JavaThread* current, Method* method)) {
110 RuntimeUpcalls::upcall_redirect(onMethodExit, current, method);
111 }
112 JRT_END
113
114 //-------------------------------RuntimeUpcalls---------------------------------------
115
116 void RuntimeUpcalls::install_upcalls(const methodHandle& method) {
117 for (int i = 0; i < RuntimeUpcallType::numTypes; i++) {
118 mark_for_upcalls(static_cast<RuntimeUpcallType>(i), method);
119 }
120 }
121
122 bool RuntimeUpcalls::register_upcall(RuntimeUpcallType upcall_type, const char* upcall_name, RuntimeUpcall upcall, RuntimeUpcallMethodFilterCallback method_filter_callback)
123 {
124 assert(upcall_type < numTypes, "invalid upcall type");
125 assert(_state == Open, "upcalls are not open for registration");
126 if (_state != Open) return false;
127 return register_upcall(upcall_type, RuntimeUpcallInfo::create(upcall_name, upcall, method_filter_callback));
128 }
129
130 RuntimeUpcallInfo* RuntimeUpcalls::get_next_upcall(RuntimeUpcallType upcall_type, MethodDetails& method_details, RuntimeUpcallInfo* prev_upcall_info) {
131 assert(upcall_type < numTypes, "invalid upcall type");
132 if (_upcalls[upcall_type] != nullptr) {
133 // simple case where there's only one upcall
134 if (_upcalls[upcall_type]->length() == 1) {
135 if (prev_upcall_info != nullptr) {
136 return nullptr;
137 }
138 RuntimeUpcallInfo* upcall = _upcalls[upcall_type]->at(0);
139 return upcall->includes(method_details) ? upcall : nullptr;
140 }
141
142 // Resume from where we left off, unless we are the last entry.
143 assert(prev_upcall_info == nullptr || (prev_upcall_info->get_index() >= 0 && prev_upcall_info->get_index() < _upcalls[upcall_type]->length()), "invalid upcall index");
144 int index = (prev_upcall_info != nullptr) ? prev_upcall_info->get_index() + 1 : 0;
145 for (int i = index; i < _upcalls[upcall_type]->length(); i++) {
146 RuntimeUpcallInfo* upcall = _upcalls[upcall_type]->at(i);
147 if (upcall->includes(method_details)) {
148 return upcall;
149 }
150 }
151 }
152
153 return nullptr;
154 }
155
156 RuntimeUpcallInfo* RuntimeUpcalls::get_first_upcall(RuntimeUpcallType upcall_type, MethodDetails& method_details) {
157 return get_next_upcall(upcall_type, method_details, nullptr);
158 }
159
160 bool RuntimeUpcalls::does_upcall_need_method_parameter(address upcall_address)
161 {
162 // Redirect needs the method parameter for filtering.
163 if((upcall_address == CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_entry_upcall_redirect)) ||
164 (upcall_address == CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_exit_upcall_redirect))) {
165 return true;
166 }
167
168 return false;
169 }
170
171 address RuntimeUpcalls::on_method_entry_upcall_address()
172 {
173 // Optimized case when there's only one upcall (no need to redirect).
174 if(_upcalls[onMethodEntry] != nullptr && _upcalls[onMethodEntry]->length() == 1) {
175 return _upcalls[onMethodEntry]->at(0)->upcall_address();
176 }
177
178 return CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_entry_upcall_redirect);
179 }
180
181 address RuntimeUpcalls::on_method_exit_upcall_address()
182 {
183 // Optimized case when there's only one upcall (no need to redirect).
184 if(_upcalls[onMethodExit] != nullptr && _upcalls[onMethodExit]->length() == 1) {
185 return _upcalls[onMethodExit]->at(0)->upcall_address();
186 }
187
188 return CAST_FROM_FN_PTR(address, RuntimeUpcalls::on_method_exit_upcall_redirect);
189 }
190
191 const char* RuntimeUpcalls::get_name_for_upcall_address(address upcall_address)
192 {
193 for(int i = 0; i < RuntimeUpcallType::numTypes; i++) {
194 if (_upcalls[i] != nullptr) {
195 for (int j = 0; j < _upcalls[i]->length(); j++) {
196 RuntimeUpcallInfo* upcall = _upcalls[i]->at(j);
197 if (upcall->upcall_address() == upcall_address) {
198 return upcall->upcall_name();
199 }
200 }
201 }
202 }
203 return nullptr;
204 }