ADB(六)_调试ADB(ADB设置自身日志的代码梳理和设置ADB自身日志可见)

10 篇文章 2 订阅
8 篇文章 13 订阅

前言

前文

ADB(一)_概况了解
ADB(二)_ADBD_main()函数代码梳理
ADB(三)_ADBD_adbd_main()函数代码梳理
ADB(四)_host端的启动流程代码梳理
ADB(五)_host端adb server相关的代码梳理

首先,我们知道ADB是间接调用并向开发人员输出log【日志】信息来对Android中的程序呢进行调试的,在Android中有log这么个类,它是专门用来对我们开发人员或者系统自带的log进行处理;ADB本身也是程序,所以我们也可以调试ADB本身;我们就可以在ADB模块的修改和优化上添加我们自己的log来调试。

接下来,主要从两个部分来对ADB(adb ,adbd)即 ADB在android device 端的adbd和host 开发主机端的adb来看,ADB的log相关设置是怎么运行的;在默认的情况下,ADB(adb,adbd)的log都是不可见的,我们又该怎么让ADB的log对开发人员可见呢?

一. adbd的调试

首先,我们看看adbd部分的log;这就要了解adbd的启动流程是怎么样的,不熟悉的同学可以先到我们之前对 ADB(二)_ADBD_main()函数代码梳理中去看看。

在熟悉了adbd的启动流程后,我们就知道,在adbd在main()函数中会在调用adb_trace_init();来设置对adbd的的调试功能的初始化;它会根据系统系统设置来开启和关闭adbd的log输出保存到指定路径的文件中,供开发人员使用。
我们还是回到源码中,源码是我们在上层一切方法的依据来源:

1. adbd_main()_adb_trace_init()

/system/core/adb/daemon/main.cpp

int main(int argc, char** argv) {
  	...
    adb_trace_init(argv);
	...
}

可以看到在在main()方法中调用了adb_trace_init()来跟踪log;那我们就到adb_trace_init()函数中看看是怎么实现的。

2. adbd_ adb_trace_init()

void adb_trace_init(char** argv) {
#if !ADB_HOST
	 // Don't open log file if no tracing, since this will block
	  // the crypto unmount of /data
	  if (!get_trace_setting().empty()) {
	      if (unix_isatty(STDOUT_FILENO) == 0) {
	          start_device_log();
	      }
	  }
#endif
	...
	android::base::InitLogging(argv, &AdbLogger);
	...
	setup_trace_mask();
	VLOG(ADB) << adb_version();
}
2.1 get_trace_setting()

在adb_trace_init()函数中,先判断ADB_HOST,由于是运行在adbd中,Android.mk文件在编译时将adbd代码中的ADB_HOST赋值为了0,所以这里!ADB_HOST就为真,接下来的代码肯定是会走的。然后通过get_trace_stting()判断trace设置是否为空,get_trace_setting()的实现如下:

std::string get_trace_setting() {
   return android::base::GetProperty("persist.adb.trace_mask", "");
#endif

我们可以看到,get_trace_setting()是根据"persist.adb.trace_mask"有没有设置来确认要不要进行adbd的log的生成;在确认设置不为空的情况下,就会调用unix_isatty()函数再次进行一个判断;具体怎判断的呢?接着看

2.2 unix_isatty()
static __inline__ int unix_isatty(int fd) {
    return isatty(fd);
}

uni_isatty()最终调用的是isatty()函数,这函数的主要功能是检查设备类型 , 判断fd是否是为终端的。这段话什么意思呢?我是这么理解的:我们的标准输出输出和标准错误输出都是fd,但是他们可以直接在终端上输入和显示出来,而其它的fd不行,这就是isatty()函数的功能。简单的来说这里就是要利用标准输出和标准错误输出来收集log。

2.3 start_device_log()

在上面的条件都满足的情况下就说明log的开关打开了,我们就可以进行log的收集了。这里是调用了 start_device_log()函数,我们看看其内部是怎么实现的:

void start_device_log(void) {
    int fd = unix_open(get_log_file_name().c_str(),
                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
    if (fd == -1) {
        return;
    }

    // Redirect stdout and stderr to the log file.
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
    unix_close(fd);
}

在start_device_log()要进行log的收集工作之前,需要找到保存log的文件及其路径,这里是调用get_log_file_name()函数来实现的:

2.3.1 get_log_file_name()
static std::string get_log_file_name() {
    struct tm now;
    time_t t;
    tzset();
    time(&t);
    localtime_r(&t, &now);

    char timestamp[PATH_MAX];
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);

    return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
                                       getpid());
}

get_log_file_name()的内部实现很简单,就是返回一个文件路径,具体的格式为/data/adb/+时间标签+当前adbd的进程ID号

2.3.2 dup2()

在保存log的文件解决的前提先,即可以往里面写数据了,不过我们这里不是直接写数据,而是使用复制的方式,将标准输出和标准错误输出的信息通过dup2()函数复制到保存log的文件fd中。

简单学习一下 dup,dup2

顺便了解一下dup,dup2两个函数的使用

#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);

😒 😒 😒 😒 😒 😒 😒 😒 😒 😒 😒 😒
int dup(old_fd) :old_fd是一个某一个打开的文件的描述符,它的返回值是当前进程可用的最小的文件描述符,返回的fd和old_fd指向的是同一个文件。
int dup2(old_fd,new_fd) :比较复杂一点,它有不同的情况,我们在这里只要知道一下它在我们这里的作用,就是将标准输出和标准错误输出重定向到指定的文件中去。

2.4 android::base::InitLogging()

这个函数是Android 提供的,主要是初始化Android系统在运行是的log,我们的adbd功能自身运行的log关联不是很大。我们先不赘述;

2.5 setup_trace_mask()

在相关的log初始化后,就会调动setup_trace_mask();这主要是对ADB(adb,adbd)的log 的mask(掩码)进行设置的。从系统的trace设置中获取标志类构建mask,其中1all标志表示语允许所有类型。

  • 其中host端的adb主要是获取ADB_TRACE环境变量来判断,
  • Android device端的adbd是通过 persist.adb.trace_mask系统属性来判断;
static void setup_trace_mask() {
    const std::string trace_setting = get_trace_setting();
    if (trace_setting.empty()) {
        return;
    }

    std::unordered_map<std::string, int> trace_flags = {
        {"1", -1},
        {"all", -1},
        {"adb", ADB},
        {"sockets", SOCKETS},
        {"packets", PACKETS},
        {"rwx", RWX},
        {"usb", USB},
        {"sync", SYNC},
        {"sysdeps", SYSDEPS},
        {"transport", TRANSPORT},
        {"jdwp", JDWP},
        {"services", SERVICES},
        {"auth", AUTH},
        {"fdevent", FDEVENT},
        {"shell", SHELL}};

    std::vector<std::string> elements = android::base::Split(trace_setting, " ");
    for (const auto& elem : elements) {
        const auto& flag = trace_flags.find(elem);
        if (flag == trace_flags.end()) {
            LOG(ERROR) << "Unknown trace flag: " << elem;
            continue;
        }

        if (flag->second == -1) {
            // -1 is used for the special values "1" and "all" that enable all
            // tracing.
            adb_trace_mask = ~0;
            break;
        } else {
            adb_trace_mask |= 1 << flag->second;
        }
    }

    if (adb_trace_mask != 0) {
        android::base::SetMinimumLogSeverity(android::base::VERBOSE);
    }
}

小结

  • 要看到设备端的adbd进程的log,从上面adb_trace_init()中我们了解到我们需要手动来设置系统属性“persist.adb.trace_mask”,我们可以通过setprop来实现
  • 设备端的adb的日志存放在/data/adb

具体操作举例:

1.设置属性
adb shell setprop persist.adb.trace_mask 1
2. 重新启动adbd
adb shell pkill adbd

二. adb的调试

和Android端的adbd类似, host端的adb的调试也需要修改配置打开,我们也从main()函数开始看起:
启动

int main(int argc, char** argv) {
    adb_trace_init(argv);
    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}

由上面的上面的代码可以发现。host端的adb也是调用adb_trace_init()函数来进行trace的初始化。我们就根据前面的梳理基础,看看host端是具体怎么设置的:

1.adb_trace_init

void adb_trace_init(char** argv) {
#if !ADB_HOST
    // Don't open log file if no tracing, since this will block
    // the crypto unmount of /data
    if (!get_trace_setting().empty()) {
        if (unix_isatty(STDOUT_FILENO) == 0) {
            start_device_log();
        }
    }
#endif

#if ADB_HOST && !defined(_WIN32)
    // adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
    // If set, move it out of the way so that libbase logging doesn't try to parse it.
    std::string log_tags;
    char* ANDROID_LOG_TAGS = getenv("ANDROID_LOG_TAGS");
    if (ANDROID_LOG_TAGS) {启动
        log_tags = ANDROID_LOG_TAGS;
        unsetenv("ANDROID_LOG_TAGS");
    }
#endif

    android::base::InitLogging(argv, &AdbLogger);

#if ADB_HOST && !defined(_WIN32)
    // Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
    if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
#endif

    setup_trace_mask();

    VLOG(ADB) << adb_version();
}

这次我们的程序是运行在host端中,根据对!ADB_HOST的判断,在Android.mk中将在host端程序中的ADB_HOST赋值为1,即"!ADB_HOST"为假,就不会走下列代码了:

#if !ADB_HOST
    // Don't open log file if no tracing, since this will block
    // the crypto unmount of /data
    if (!get_trace_setting().empty()) {
        if (unix_isatty(STDOUT_FILENO) == 0) {
            start_device_log();
        }
    }
#endif

但是相应的, host 端会增加相应的操作:如下所示

void adb_trace_init(char** argv) {
...
#if ADB_HOST && !defined(_WIN32)
    // adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
    // If set, move it out of the way so that libbase logging doesn't try to parse it.
    std::string log_tags;
    char* ANDROID_LOG_TAGS = getenv("ANDROID_LOG_TAGS");
    if (ANDROID_LOG_TAGS) {
        log_tags = ANDROID_LOG_TAGS;
        unsetenv("ANDROID_LOG_TAGS");
    }
#endif
    android::base::InitLogging(argv, &AdbLogger);
#if ADB_HOST && !defined(_WIN32)
    // Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
    if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
#endif
...
}

如上述代码,

  • 首先会先获取环境变量“ANDROID_LOG_TAGS”,根据注解,我们了解到,之前的adb会忽略$ANDROID_LOG_TAGS,但是依然会把它传递给logcat,如果系统中设置了变量“ANDROID_LOG_TAGS”,那我们就要移除它,这样libbase的logging就不会解析它。

  • 然后在logcat相关初始化完成再将"ANDROID_LOG_TAGS"变量恢复成原来的状态。

1.1 setup_trace_mask

这个和上面的adbd的log掩码操作类似;主要的区别在于系统环境变量“ADB_TRACE”的获取和adbd中有所不同。
到这里,我们就会发现,host端的adb和设备端的adbd的日志掩码设置相同,但是log初始化不一样,我们并没有在host端的adb_trace_init()中发现哪里有设置log的输出。所以,host端的log应该是在其他的地方设置的,我们就接着看看。

2.setup_daemon_logging

在前篇ADB(五)_host端adb server相关的代码梳理中,我们看到有个setup_daemon_logging()函数,当时并没有深入,现在我们要好好看看这个函数的实现了;

static void setup_daemon_logging() {
    const std::string log_file_path(GetLogFilePath());
    int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
    if (fd == -1) {
        fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
    }
    if (dup2(fd, STDOUT_FILENO) == -1) {
        fatal("cannot redirect stdout: %s", strerror(errno));
    }
    if (dup2(fd, STDERR_FILENO) == -1) {
        fatal("cannot redirect stderr: %s", strerror(errno));
    }
    unix_close(fd);

    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
    LOG(INFO) << adb_version();
}

2.1 GetLogFilePath

在setup_daemon_logging()函数中,首先会调用GetLogFilePath()函数来获取log文件的路径;关获取文件路径的操作是肯定适合所在的系统相关的,那我们来看看这个GetLogFilePath()函数的实现是怎么样的:

std::string GetLogFilePath() {
#if defined(_WIN32)
    const char log_name[] = "adb.log";
    WCHAR temp_path[MAX_PATH];
    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
    if (nchars >= arraysize(temp_path) || nchars == 0) {
        // If string truncation or some other error.
        fatal("cannot retrieve tsetup_trace_maskemporary file path: %s\n",
              android::base::SystemErrorCodeToString(GetLastError()).c_str());
    }
    std::string temp_path_utf8;
    if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
    }
    return temp_path_utf8 + log_name;
#else
    const char* tmp_dir = getenv("TMPDIR");
    if (tmp_dir == nullptr) tmp_dir = "/tmp";
    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
#endif
}

如上代码,我们就linux系统而言,在linux的系统中,可以看到,adb server的log保存的路径为/tmp;名称为"adb."+"getuid()"+"log"; getuid()获取的是当前进程的用户识别码。

在获取到保存log的路径和名称后,接下来的操作就调用unix_open()【封装了open()】函数来打开文件,然后。。。。。。。恩恩额。怎么似曾相识??是的,接下来的操作和上面在分析adbd中的start_device_log()函数操作相同。

小结

  • 要看到host端的adb进程的log,从上面梳理中我们了解到我们需要手动来设置环境变量“ADB_TRACE”,我们可以通过export来实现
  • host端的adb的日志存放在‘/tmp,

具体操作举例【linux系统】:

1. 杀死原adb进程
adb kill-server
2. 设置环境变量
export ADB_TRACE =1
3.重新启动adb
adb start-server

比较

比较host端的adb的log的初始化和设备端的adbd的log的初始化流程,会发现,两者的流程很相似。都是将标准输出和标准的错误输出数据dup到log文件中。然后log的优先级分别使用环境变量ADB_TRACEpersist.adb.trace_mask来控制,默认情况下两者的log都是不输出的。
我们只要将相应的环境变量(对adndroid来说就是系统属性)设置成对应的数据,

具体参考如下:

    std::unordered_map<std::string, int> trace_flags = {
        {"1", -1},
        {"all", -1},
        {"adb", ADB},
        {"sockets", SOCKETS},
        {"packets", PACKETS},
        {"rwx", RWX},
        {"usb", USB},
        {"sync", SYNC},
        {"sysdeps", SYSDEPS},
        {"transport", TRANSPORT},
        {"jdwp", JDWP},
        {"services", SERVICES},
        {"auth", AUTH},
        {"fdevent", FDEVENT},
        {"shell", SHELL}};
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值