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