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/cdsConfig.hpp"
 26 #include "cds/cdsEndTrainingUpcall.hpp"
 27 #include "cds/metaspaceShared.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       Atomic::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 (Atomic::cmpxchg(&_triggered, 0, 1) == 0) {
 77       MetaspaceShared::preload_and_dump(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 }