Home | History | Annotate | Download | only in jni
      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:
     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 
     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     }
     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     }
     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     }
     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     }
    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 
    134     static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
    135         readEvents(env, loggerMode, nullptr, startTime, out);
    136     }
    137 
    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