cpp/JNILoggerHelpers.h (90 lines of code) (raw):
/**
* Copyright 2004-present, Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <profilo/LogEntry.h>
#include <profilo/entries/Entry.h>
#include <profilo/util/common.h>
#include <cstdint>
#ifdef __ANDROID__
#include <fb/Build.h>
#endif
#include <fbjni/fbjni.h>
namespace fbjni = facebook::jni;
namespace facebook {
namespace profilo {
using namespace entries;
namespace logger_jni {
// These flags should match the ones from Logger.java
static constexpr uint32_t FILL_TIMESTAMP = 1 << 1;
static constexpr uint32_t FILL_TID = 1 << 2;
template <typename LoggerLike>
jint writeStandardEntryFromJNI(
LoggerLike& loggerLike,
jint flags,
jint type,
jlong timestamp,
jint tid,
jint arg1,
jint arg2,
jlong arg3) {
if (flags & FILL_TIMESTAMP) {
timestamp = monotonicTime();
}
if (flags & FILL_TID) {
tid = threadID();
}
StandardEntry entry{
.id = 0,
.type = static_cast<decltype(StandardEntry::type)>(type),
.timestamp = timestamp,
.tid = tid,
.callid = arg1,
.matchid = arg2,
.extra = arg3,
};
return loggerLike.write(entry);
}
template <typename LoggerLike>
jint writeBytesEntryFromJNI(
LoggerLike& loggerLike,
jint flags,
jint type,
jint arg1,
jstring arg2) {
if (arg2 == nullptr) {
constexpr uint8_t bytes[] = "null";
constexpr int bytesLen = 4;
return loggerLike.writeBytes(
static_cast<EntryType>(type), arg1, bytes, bytesLen);
}
// Android 8.0 and above can issue syscalls during Get/ReleaseStringCritical,
// causing them to be much slower than the always-copy GetStringChars
// version. Therefore, we use GetStringCritical
// before 8.0 and GetStringChars after.
#ifdef __ANDROID__
static bool jniUseCritical = build::Build::getAndroidSdk() < 26;
#else
static bool jniUseCritical = true;
#endif
auto env = fbjni::Environment::current();
constexpr auto kMaxJavaStringLength = 512;
auto len = std::min(env->GetStringLength(arg2), kMaxJavaStringLength);
uint8_t bytes[len]; // we're filtering to ASCII so one char must be one byte
{
// JStringUtf16Extractor is using GetStringCritical to give us raw jchar*.
// We then filter down the wide chars to uint8_t ASCII.
const jchar* str = nullptr;
if (jniUseCritical) {
str = env->GetStringCritical(arg2, nullptr);
} else {
str = env->GetStringChars(arg2, nullptr);
}
for (int i = 0; i < len; i++) {
if (str[i] < 128) {
bytes[i] = str[i];
} else {
bytes[i] = '.';
}
}
if (jniUseCritical) {
env->ReleaseStringCritical(arg2, str);
} else {
env->ReleaseStringChars(arg2, str);
}
}
return loggerLike.writeBytes(static_cast<EntryType>(type), arg1, bytes, len);
}
void registerNatives();
} // namespace logger_jni
} // namespace profilo
} // namespace facebook