1 /*
  2  * Copyright (c) 2016, 2025, 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 "jfr/jni/jfrJavaSupport.hpp"
 26 #include "jfr/utilities/jfrJavaLog.hpp"
 27 #include "jfr/utilities/jfrLogTagSets.hpp"
 28 #include "logging/log.hpp"
 29 #include "logging/logConfiguration.hpp"
 30 #include "logging/logMessage.hpp"
 31 #include "memory/resourceArea.hpp"
 32 #include "oops/objArrayOop.inline.hpp"
 33 #include "runtime/javaThread.hpp"
 34 
 35 #define JFR_LOG_TAGS_CONCATED(T0, T1, T2, T3, T4, T5, ...)  \
 36   T0 ## _ ## T1 ## _ ## T2 ## _ ## T3 ## _ ## T4 ## _ ## T5
 37 
 38 enum JfrLogTagSetType {
 39 #define JFR_LOG_TAG(...) \
 40     EXPAND_VARARGS(JFR_LOG_TAGS_CONCATED(__VA_ARGS__, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG)),
 41 
 42     JFR_LOG_TAG_SET_LIST
 43 
 44 #undef JFR_LOG_TAG
 45     JFR_LOG_TAG_SET_COUNT
 46 };
 47 
 48 struct jfrLogSubscriber
 49 {
 50   jobject log_tag_enum_ref;
 51   LogTagSet* log_tag_set;
 52 };
 53 
 54 static jfrLogSubscriber log_tag_sets[JFR_LOG_TAG_SET_COUNT];
 55 
 56 static void log_cfg_update(LogLevelType llt, JfrLogTagSetType jflt, TRAPS) {
 57   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
 58   if (log_tag_sets[jflt].log_tag_enum_ref == nullptr) {
 59     return;
 60   }
 61   jobject lt = log_tag_sets[jflt].log_tag_enum_ref;
 62   // set field tagSetLevel to llt value
 63   JavaValue result(T_VOID);
 64   JfrJavaArguments args(&result);
 65   args.set_klass(JfrJavaSupport::klass(lt));
 66   args.set_name("tagSetLevel");
 67   args.set_signature("I");
 68   args.set_receiver(JfrJavaSupport::resolve_non_null(lt));
 69   args.push_int(llt);
 70   JfrJavaSupport::set_field(&args, THREAD);
 71 }
 72 
 73 static LogLevelType highest_level(const LogTagSet& lts) {
 74   for (size_t i = 0; i < LogLevel::Count; i++) {
 75     if (lts.is_level((LogLevelType)i)) {
 76       return (LogLevelType)i;
 77     }
 78   }
 79   return LogLevel::Off;
 80 }
 81 
 82 static void log_config_change_internal(bool init, TRAPS) {
 83   LogLevelType llt;
 84   LogTagSet* lts;
 85 
 86 #define JFR_LOG_TAG(...) \
 87   lts = &LogTagSetMapping<LOG_TAGS(__VA_ARGS__)>::tagset(); \
 88   if (init) { \
 89     JfrLogTagSetType tagSetType = \
 90       EXPAND_VARARGS(JFR_LOG_TAGS_CONCATED(__VA_ARGS__, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG)); \
 91     assert(nullptr == log_tag_sets[tagSetType].log_tag_set, "Init JFR LogTagSets twice"); \
 92     log_tag_sets[tagSetType].log_tag_set = lts; \
 93   } \
 94   llt = highest_level(*lts); \
 95   log_cfg_update(llt, \
 96   EXPAND_VARARGS(JFR_LOG_TAGS_CONCATED(__VA_ARGS__, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG, _NO_TAG)), THREAD);
 97   JFR_LOG_TAG_SET_LIST
 98 #undef JFR_LOG_TAG
 99 }
100 
101 static void log_config_change() {
102   JavaThread* t = JavaThread::current();
103   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(t));
104   log_config_change_internal(false, t);
105 }
106 
107 void JfrJavaLog::subscribe_log_level(jobject log_tag, jint id, TRAPS) {
108   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
109   static bool subscribed_updates = true;
110   assert(id < JFR_LOG_TAG_SET_COUNT,
111     "LogTag id, java and native not in synch, %d < %d", id, JFR_LOG_TAG_SET_COUNT);
112   assert(nullptr == log_tag_sets[id].log_tag_enum_ref, "Subscribing twice");
113   log_tag_sets[id].log_tag_enum_ref = JfrJavaSupport::global_jni_handle(log_tag, THREAD);
114   if (subscribed_updates) {
115     LogConfiguration::register_update_listener(&log_config_change);
116     log_config_change_internal(true, THREAD);
117     subscribed_updates = false;
118   } else {
119     log_config_change_internal(false, THREAD);
120   }
121 }
122 
123 void JfrJavaLog::log_event(JNIEnv* env, jint level, jobjectArray lines, bool system, TRAPS) {
124   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
125   if (lines == nullptr) {
126     return;
127   }
128   if (level < (jint)LogLevel::First || level > (jint)LogLevel::Last) {
129     JfrJavaSupport::throw_illegal_argument_exception("LogLevel passed is outside valid range", THREAD);
130     return;
131   }
132 
133   objArrayOop the_lines = objArrayOop(JfrJavaSupport::resolve_non_null(lines));
134   assert(the_lines != nullptr, "invariant");
135   assert(the_lines->is_array(), "must be array");
136   const int length = the_lines->length();
137 
138   ResourceMark rm(THREAD);
139   LogMessage(jfr, event) jfr_event;
140   LogMessage(jfr, system, event) jfr_event_system;
141   for (int i = 0; i < length; ++i) {
142     const char* text = JfrJavaSupport::c_str(the_lines->obj_at(i), THREAD);
143     if (text == nullptr) {
144       // An oome has been thrown and is pending.
145       return;
146     }
147     if (system) {
148       jfr_event_system.write((LogLevelType)level, "%s", text);
149     } else {
150       jfr_event.write((LogLevelType)level, "%s", text);
151     }
152   }
153 }
154 
155 void JfrJavaLog::log(jint tag_set, jint level, jstring message, TRAPS) {
156   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
157   if (message == nullptr) {
158     return;
159   }
160   if (level < (jint)LogLevel::First || level > (jint)LogLevel::Last) {
161     JfrJavaSupport::throw_illegal_argument_exception("LogLevel passed is outside valid range", THREAD);
162     return;
163   }
164   if (tag_set < 0 || tag_set >= (jint)JFR_LOG_TAG_SET_COUNT) {
165     JfrJavaSupport::throw_illegal_argument_exception("LogTagSet id is outside valid range", THREAD);
166     return;
167   }
168   ResourceMark rm(THREAD);
169   const char* const s = JfrJavaSupport::c_str(message, CHECK);
170   assert(s != nullptr, "invariant");
171   assert(log_tag_sets[tag_set].log_tag_set != nullptr, "LogTagSet is not init");
172   log_tag_sets[tag_set].log_tag_set->log((LogLevelType)level, s);
173 }