1. ProtoLog如何动态打开(android R开始引入)
类似源码里面的 ProtoLog.v ,在userdebug 版本可以动态打开
ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation continue waiting for draw in %s", w);
具体可以参考frameworks/base/tools/protologtool/README.md 如类似下面的ProtoLog
ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
将会给ProtoLogTool 转换成
if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
int protoLogParam0 = value1;
String protoLogParam1 = String.valueOf(value2);
ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, 0b0100, "Format string %d %s or null", protoLogParam0, protoLogParam1);
}
ps: 它有两种log形式: => 第一种写入到db中 举例 adb shell $ cmd window logging enable WM_DEBUG_ORIENTATION $ cmd window logging start 开始log的输出 =>Start logging to /data/misc/wmtrace/wm_log.pb. $ cmd window logging stop 停止log的输出 Stop logging to /data/misc/wmtrace/wm_log.pb. Waiting for log to flush. Log written to /data/misc/wmtrace/wm_log.pb. => 第二种输出到logcat 举例: 这个是直接保存在logcat中 $ cmd window logging enable-text WM_DEBUG_ORIENTATION 使用logcat就可以直接看得到日志输出 $ adb logcat -b all | egrep -i Orientation
2. 关于代码里面写的isLoggable,开关如何打开(很早就有了)
例如NotificationManagerService 的DBG 如何默认开呢?
public class NotificationManagerService extends AbsNotificationManagerService {
public static final String TAG = "NotificationService";
public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
方法是:设置系统属性persist.log.tag.NotificationService 为 V ,然后重启手机。即可生效(user 版本一样可以生效) 这里用的是liblog 自带的根据tag 过滤是否输出日志的功能
Log.isLoggable 其实调用的是native 的方法
public static native boolean isLoggable(@Nullable String tag, @Level int level);
jni 调用isLoggable 相当于android_util_Log_isLoggable
static const JNINativeMethod gMethods[] = {
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
};
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
if (tag == NULL) {
return false;
}
const char* chars = env->GetStringUTFChars(tag, NULL);
if (!chars) {
return false;
}
jboolean result = isLoggable(chars, level);
env->ReleaseStringUTFChars(tag, chars);
return result;
}
static jboolean isLoggable(const char* tag, jint level) {
return __android_log_is_loggable(level, tag, ANDROID_LOG_INFO);
}
__android_log_is_loggable 的实现是liblog 的__android_log_is_loggable_len
int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
auto len = tag ? strlen(tag) : 0;
return __android_log_is_loggable_len(prio, tag, len, default_prio);
}
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
int minimum_log_priority = __android_log_get_minimum_priority();
int property_log_level = __android_log_level(tag, len);
if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) {
return prio >= std::min(property_log_level, minimum_log_priority);
} else if (property_log_level >= 0) {
return prio >= property_log_level;
} else if (minimum_log_priority != ANDROID_LOG_DEFAULT) {
return prio >= minimum_log_priority;
} else {
return prio >= default_prio;
}
}
static int __android_log_level(const char* tag, size_t len) {
static const char log_namespace[] = "persist.log.tag.";
static const size_t base_offset = 8;
if (tag == nullptr || len == 0) {
auto& tag_string = GetDefaultTag();
tag = tag_string.c_str();
len = tag_string.size();
}
char key[sizeof(log_namespace) + len];
char* kp;
size_t i;
char c = 0;
static char* last_tag;
static size_t last_tag_len;
static uint32_t global_serial;
uint32_t current_global_serial = 0;
static struct cache_char tag_cache[2];
static struct cache_char global_cache[2];
int change_detected;
int global_change_detected;
int not_locked;
strcpy(key, log_namespace);
global_change_detected = change_detected = not_locked = lock();
if (!not_locked) {
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
if (check_cache(&tag_cache[i].cache)) {
change_detected = 1;
}
}
for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
if (check_cache(&global_cache[i].cache)) {
global_change_detected = 1;
}
}
current_global_serial = __system_property_area_serial();
if (current_global_serial != global_serial) {
change_detected = 1;
global_change_detected = 1;
}
}
if (len) {
int local_change_detected = change_detected;
if (!not_locked) {
if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
tag_cache[i].cache.pinfo = NULL;
tag_cache[i].c = '\0';
}
if (last_tag) last_tag[0] = '\0';
local_change_detected = 1;
}
if (!last_tag || !last_tag[0]) {
if (!last_tag) {
last_tag = static_cast<char*>(calloc(1, len + 1));
last_tag_len = 0;
if (last_tag) last_tag_len = len + 1;
} else if (len >= last_tag_len) {
last_tag = static_cast<char*>(realloc(last_tag, len + 1));
last_tag_len = 0;
if (last_tag) last_tag_len = len + 1;
}
if (last_tag) {
strncpy(last_tag, tag, len);
last_tag[len] = '\0';
}
}
}
strncpy(key + sizeof(log_namespace) - 1, tag, len);
key[sizeof(log_namespace) - 1 + len] = '\0';
kp = key;
for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
struct cache_char* cache = &tag_cache[i];
struct cache_char temp_cache;
if (not_locked) {
temp_cache.cache.pinfo = NULL;
temp_cache.c = '\0';
cache = &temp_cache;
}
if (local_change_detected) {
refresh_cache(cache, kp);
}
if (cache->c) {
c = cache->c;
break;
}
kp = key + base_offset;
}
}
switch (toupper(c)) {
case 'V':
case 'D':
case 'I':
case 'W':
case 'E':
case 'F':
case 'A':
case 'S':
case BOOLEAN_FALSE:
break;
default:
key[sizeof(log_namespace) - 2] = '\0';
kp = key;
for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
struct cache_char* cache = &global_cache[i];
struct cache_char temp_cache;
if (not_locked) {
temp_cache = *cache;
if (temp_cache.cache.pinfo != cache->cache.pinfo) {
temp_cache.cache.pinfo = NULL;
temp_cache.c = '\0';
}
cache = &temp_cache;
}
if (global_change_detected) {
refresh_cache(cache, kp);
}
if (cache->c) {
c = cache->c;
break;
}
kp = key + base_offset;
}
break;
}
if (!not_locked) {
global_serial = current_global_serial;
unlock();
}
switch (toupper(c)) {
case 'V': return ANDROID_LOG_VERBOSE;
case 'D': return ANDROID_LOG_DEBUG;
case 'I': return ANDROID_LOG_INFO;
case 'W': return ANDROID_LOG_WARN;
case 'E': return ANDROID_LOG_ERROR;
case 'F':
case 'A': return ANDROID_LOG_FATAL;
case BOOLEAN_FALSE:
case 'S': return ANDROID_LOG_SILENT;
}
return -1;
}
3. ams wms等日志的动态打开
默认源码不支持这类动态日志,需要我们自己加入代码
用反射拿到带DEBUG_ 的一些调试变量
Field[] fields_am = ActivityManagerDebugConfig.class.getDeclaredFields();
Field[] fields_atm = ActivityTaskManagerDebugConfig.class.getDeclaredFields();
Field[] fields_thread = ActivityThread.class.getDeclaredFields();
Field[] fields = WindowManagerDebugConfig.class.getDeclaredFields();
Field[] fieldsPolicy = PhoneWindowManager.class.getDeclaredFields();
找到DEBUG_ 相关的变量,类似于ActivityThread.DEBUG_BROADCAST 、ActivityManagerDebugConfig.DEBUG_BROADCAST ,并将其默认值记录下来,此处是先放入debugValue
int bitLocation = 0;
long debugValue = 0;
for (int i = 0; i < fields_thread.length; ++i) {
fieldName = fields_thread[i].getName();
if (fieldName == null) continue;
if (fieldName.startsWith("DEBUG_") || fieldName.equals("localLOGV")) {
try {
fields_thread[i].setAccessible(true);
if (fields_thread[i].getBoolean(null)) {
debugValue = (debugValue | (1 << bitLocation));
}
bitLocation++;
} catch (IllegalAccessException e) {
pw.println("enableAmsLog exception4:" + e);
}
}
}
调用触发,可以参考原生的 adb shell dumpsys activity p 自己设置一个 adb shell dumpsys debuglog amslog enable DEBUG_BROADCAST 进行参数识别,由于除了系统进程,部分还和app进程相关,保存设置到自定义的系统属性里面persist.sys.debug.ams.log
for (int i = 0; i < fields_thread.length; ++i) {
fieldName = fields_thread[i].getName();
if (fieldName == null) continue;
try {
if (fieldName.startsWith("DEBUG_") || fieldName.equals("localLOGV")) {
if (setAll || fieldName.equals(cmd)) {
isChange = true;
fields_thread[i].setAccessible(true);
fields_thread[i].setBoolean(null, isEnable);
if (isEnable) {
debugValue = (debugValue | (1 << bitLocation));
} else {
debugValue = (debugValue & (~(1 << bitLocation)));
}
pw.println(String.format(" ActivityThread.%s = %b", fieldName, fields_thread[i].getBoolean(null)));
if (!setAll) {
break;
}
}
bitLocation++;
}
} catch (IllegalAccessException e) {
pw.println("enable exception:" + e);
}
}
String debugHexValue = "0x" + Long.toHexString(debugValue);
SystemProperties.set("persist.sys.debug.ams.log", debugHexValue);
遍历mLruProcesses 分发到android 的每个进程里面去
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r != null && r.thread != null) {
r.thread.updateDebugLog(key);
}
}
public final void updateDebugLog(int func) {
DebugLogManager.updateDebugLog(func);
}
public static void updateDebugLog(int func) {
try {
String fieldName = "";
Field[] fields = null;
int bitLocation = 0;
long setDebugValue = -1;
switch (func) {
case FUNC_ACTIVITY_LOG:
setDebugValue = SystemProperties.getLong("persist.sys.debug.ams.log", -1);
if (setDebugValue >= 0) {
fields = ActivityThread.class.getDeclaredFields();
bitLocation = 0;
for (int i = 0; i < fields.length; ++i) {
fieldName = fields[i].getName();
if (fieldName == null) continue;
if (fieldName.startsWith("DEBUG_") || fieldName.equals("localLOGV")) {
fields[i].setAccessible(true);
if ((setDebugValue & (1 << bitLocation)) == 0) {
fields[i].setBoolean(null, false);
} else {
fields[i].setBoolean(null, true);
}
bitLocation++;
}
}
}
break;
类似于ActivityThread.DEBUG_BROADCAST 、ActivityManagerDebugConfig.DEBUG_BROADCAST 的动态设置将不是问题
4. 其它动态日志
其它各个模块也有很多方式可以做动态日志,这里只是一个抛砖引玉,注意做动态日志的时候不要影响到性能了,毕竟真实用户是用不到的
|