1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ 18 #define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ 19 20 #include <memory> 21 22 #include <fcntl.h> 23 24 #include <android-base/macros.h> 25 #include <log/log_event_list.h> 26 27 #include <log/log.h> 28 29 #include <nativehelper/JNIHelp.h> 30 #include <nativehelper/ScopedLocalRef.h> 31 #include <nativehelper/ScopedPrimitiveArray.h> 32 #include <nativehelper/ScopedUtfChars.h> 33 #include "core_jni_helpers.h" 34 #include "jni.h" 35 36 namespace android { 37 38 template <log_id_t LogID, const char* EventClassDescriptor> 39 class EventLogHelper { 40 public: Init(JNIEnv * env)41 static void Init(JNIEnv* env) { 42 struct { const char *name; jclass *clazz; } gClasses[] = { 43 { EventClassDescriptor, &gEventClass }, 44 { "java/lang/Integer", &gIntegerClass }, 45 { "java/lang/Long", &gLongClass }, 46 { "java/lang/Float", &gFloatClass }, 47 { "java/lang/String", &gStringClass }, 48 { "java/util/Collection", &gCollectionClass }, 49 }; 50 struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { 51 { &gIntegerClass, "value", "I", &gIntegerValueID }, 52 { &gLongClass, "value", "J", &gLongValueID }, 53 { &gFloatClass, "value", "F", &gFloatValueID }, 54 }; 55 struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { 56 { &gEventClass, "<init>", "([B)V", &gEventInitID }, 57 { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, 58 }; 59 60 for (size_t i = 0; i < NELEM(gClasses); ++i) { 61 ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, gClasses[i].name)); 62 *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz.get()); 63 } 64 for (size_t i = 0; i < NELEM(gFields); ++i) { 65 *gFields[i].id = GetFieldIDOrDie(env, 66 *gFields[i].c, gFields[i].name, gFields[i].ft); 67 } 68 69 for (size_t i = 0; i < NELEM(gMethods); ++i) { 70 *gMethods[i].id = GetMethodIDOrDie(env, 71 *gMethods[i].c, gMethods[i].name, gMethods[i].mt); 72 } 73 } 74 writeEventInteger(JNIEnv * env ATTRIBUTE_UNUSED,jobject clazz ATTRIBUTE_UNUSED,jint tag,jint value)75 static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, 76 jint tag, jint value) { 77 android_log_event_list ctx(tag); 78 ctx << (int32_t)value; 79 return ctx.write(LogID); 80 } writeEventLong(JNIEnv * env ATTRIBUTE_UNUSED,jobject clazz ATTRIBUTE_UNUSED,jint tag,jlong value)81 static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, 82 jint tag, jlong value) { 83 android_log_event_list ctx(tag); 84 ctx << (int64_t)value; 85 return ctx.write(LogID); 86 } writeEventFloat(JNIEnv * env ATTRIBUTE_UNUSED,jobject clazz ATTRIBUTE_UNUSED,jint tag,jfloat value)87 static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, 88 jint tag, jfloat value) { 89 android_log_event_list ctx(tag); 90 ctx << (float)value; 91 return ctx.write(LogID); 92 } writeEventString(JNIEnv * env,jobject clazz ATTRIBUTE_UNUSED,jint tag,jstring value)93 static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag, 94 jstring value) { 95 android_log_event_list ctx(tag); 96 // Don't throw NPE -- I feel like it's sort of mean for a logging function 97 // to be all crashy if you pass in NULL -- but make the NULL value explicit. 98 ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL"); 99 return ctx.write(LogID); 100 } writeEventArray(JNIEnv * env,jobject clazz ATTRIBUTE_UNUSED,jint tag,jobjectArray value)101 static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag, 102 jobjectArray value) { 103 android_log_event_list ctx(tag); 104 105 if (value == nullptr) { 106 ctx << "[NULL]"; 107 return ctx.write(LogID); 108 } 109 110 jsize copied = 0, num = env->GetArrayLength(value); 111 for (; copied < num && copied < 255; ++copied) { 112 if (ctx.status()) break; 113 ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied)); 114 if (item == nullptr) { 115 ctx << "NULL"; 116 } else if (env->IsInstanceOf(item.get(), gStringClass)) { 117 ctx << ScopedUtfChars(env, (jstring) item.get()).c_str(); 118 } else if (env->IsInstanceOf(item.get(), gIntegerClass)) { 119 ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID); 120 } else if (env->IsInstanceOf(item.get(), gLongClass)) { 121 ctx << (int64_t)env->GetLongField(item.get(), gLongValueID); 122 } else if (env->IsInstanceOf(item.get(), gFloatClass)) { 123 ctx << (float)env->GetFloatField(item.get(), gFloatValueID); 124 } else { 125 jniThrowException(env, 126 "java/lang/IllegalArgumentException", 127 "Invalid payload item type"); 128 return -1; 129 } 130 } 131 return ctx.write(LogID); 132 } 133 readEvents(JNIEnv * env,int loggerMode,jlong startTime,jobject out)134 static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) { 135 readEvents(env, loggerMode, nullptr, startTime, out); 136 } 137 readEvents(JNIEnv * env,int loggerMode,jintArray jTags,jlong startTime,jobject out)138 static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime, 139 jobject out) { 140 std::unique_ptr<struct logger_list, decltype(&android_logger_list_close)> logger_list( 141 nullptr, android_logger_list_close); 142 if (startTime) { 143 logger_list.reset(android_logger_list_alloc_time(loggerMode, 144 log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0)); 145 } else { 146 logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0)); 147 } 148 if (!logger_list) { 149 jniThrowIOException(env, errno); 150 return; 151 } 152 153 if (!android_logger_open(logger_list.get(), LogID)) { 154 jniThrowIOException(env, errno); 155 return; 156 } 157 158 ScopedIntArrayRO tags(env); 159 if (jTags != nullptr) { 160 tags.reset(jTags); 161 } 162 163 while (1) { 164 log_msg log_msg; 165 int ret = android_logger_list_read(logger_list.get(), &log_msg); 166 167 if (ret == 0) { 168 return; 169 } 170 if (ret < 0) { 171 if (ret == -EINTR) { 172 continue; 173 } 174 if (ret == -EINVAL) { 175 jniThrowException(env, "java/io/IOException", "Event too short"); 176 } else if (ret != -EAGAIN) { 177 jniThrowIOException(env, -ret); // Will throw on return 178 } 179 return; 180 } 181 182 if (log_msg.id() != LogID) { 183 continue; 184 } 185 186 int32_t tag = * (int32_t *) log_msg.msg(); 187 188 if (jTags != nullptr) { 189 bool found = false; 190 for (size_t i = 0; !found && i < tags.size(); ++i) { 191 found = (tag == tags[i]); 192 } 193 if (!found) { 194 continue; 195 } 196 } 197 198 jsize len = ret; 199 ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(len)); 200 if (array == nullptr) { 201 return; 202 } 203 204 { 205 ScopedByteArrayRW bytes(env, array.get()); 206 memcpy(bytes.get(), log_msg.buf, len); 207 } 208 209 ScopedLocalRef<jobject> event(env, 210 env->NewObject(gEventClass, gEventInitID, array.get())); 211 if (event == nullptr) { 212 return; 213 } 214 215 env->CallBooleanMethod(out, gCollectionAddID, event.get()); 216 if (env->ExceptionCheck() == JNI_TRUE) { 217 return; 218 } 219 } 220 } 221 222 private: 223 static jclass gCollectionClass; 224 static jmethodID gCollectionAddID; 225 226 static jclass gEventClass; 227 static jmethodID gEventInitID; 228 229 static jclass gIntegerClass; 230 static jfieldID gIntegerValueID; 231 232 static jclass gLongClass; 233 static jfieldID gLongValueID; 234 235 static jclass gFloatClass; 236 static jfieldID gFloatValueID; 237 238 static jclass gStringClass; 239 }; 240 241 // Explicit instantiation declarations. 242 template <log_id_t LogID, const char* EventClassDescriptor> 243 jclass EventLogHelper<LogID, EventClassDescriptor>::gCollectionClass; 244 template <log_id_t LogID, const char* EventClassDescriptor> 245 jmethodID EventLogHelper<LogID, EventClassDescriptor>::gCollectionAddID; 246 247 template <log_id_t LogID, const char* EventClassDescriptor> 248 jclass EventLogHelper<LogID, EventClassDescriptor>::gEventClass; 249 template <log_id_t LogID, const char* EventClassDescriptor> 250 jmethodID EventLogHelper<LogID, EventClassDescriptor>::gEventInitID; 251 252 template <log_id_t LogID, const char* EventClassDescriptor> 253 jclass EventLogHelper<LogID, EventClassDescriptor>::gIntegerClass; 254 template <log_id_t LogID, const char* EventClassDescriptor> 255 jfieldID EventLogHelper<LogID, EventClassDescriptor>::gIntegerValueID; 256 257 template <log_id_t LogID, const char* EventClassDescriptor> 258 jclass EventLogHelper<LogID, EventClassDescriptor>::gLongClass; 259 template <log_id_t LogID, const char* EventClassDescriptor> 260 jfieldID EventLogHelper<LogID, EventClassDescriptor>::gLongValueID; 261 262 template <log_id_t LogID, const char* EventClassDescriptor> 263 jclass EventLogHelper<LogID, EventClassDescriptor>::gFloatClass; 264 template <log_id_t LogID, const char* EventClassDescriptor> 265 jfieldID EventLogHelper<LogID, EventClassDescriptor>::gFloatValueID; 266 267 template <log_id_t LogID, const char* EventClassDescriptor> 268 jclass EventLogHelper<LogID, EventClassDescriptor>::gStringClass; 269 270 } // namespace android 271 272 #endif // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ 273