源码分析
#include "monitor.h"#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>#include <log/log.h>
#include <log/log_event_list.h>#include "art_method.h"
#include "jni/jni_env_ext.h"
#include "palette/palette.h"
#include "thread.h"// 定义了一个日志标签 EVENT_LOG_TAG_dvm_lock_sample,用于标识锁竞争事件的日志
#define EVENT_LOG_TAG_dvm_lock_sample 20003namespace art HIDDEN {// Thread* self:当前线程对象。
// uint32_t wait_ms:线程等待锁的时间(毫秒)。
// uint32_t sample_percent:采样百分比。
// ArtMethod* owner_method:锁持有者的方法。
// uint32_t owner_dex_pc:锁持有者的代码位置(Dex 文件中的程序计数器)。
void Monitor::LogContentionEvent(Thread* self,uint32_t wait_ms,uint32_t sample_percent,ArtMethod* owner_method,uint32_t owner_dex_pc) {// 创建了一个日志上下文对象 ctx,用于记录锁竞争事件的日志。android_log_event_list ctx(EVENT_LOG_TAG_dvm_lock_sample);const char* owner_filename;int32_t owner_line_number;// 使用 TranslateLocation 函数将方法和程序计数器(PC)转换为文件名和行号TranslateLocation(owner_method, owner_dex_pc, &owner_filename, &owner_line_number);// 从 /proc/self/cmdline 文件中读取当前进程的名称,并将其记录到日志中// Emit the process name, <= 33 bytes.char proc_name[33] = {};{int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);read(fd, proc_name, sizeof(proc_name) - 1);close(fd);ctx << proc_name;}// Emit the sensitive thread ("main thread") status. We follow tradition that this corresponds// to a C++ bool's value, but be explicit.// 记录当前线程是否为“敏感线程”。在 Android 系统中,主线程(main thread)通常被认为是“敏感线程”,// 因为它负责处理 UI 事件和用户交互。如果主线程被阻塞(例如等待锁),可能会导致应用程序响应缓慢甚至出现 // ANR(Application Not Responding)错误。// 遵循了一个传统做法,即用 C++ 的布尔值(bool)来表示线程是否为敏感线程。// 在 C++ 中,true 对应于非零值(通常是 1),而 false 对应于 0// 尽管布尔值的表示是约定俗成的,但为了代码的可读性和明确性,这里通过定义常量来显式地表示布尔值constexpr uint32_t kIsSensitive = 1u;constexpr uint32_t kIsNotSensitive = 0u;ctx << (Thread::IsSensitiveThread() ? kIsSensitive : kIsNotSensitive);// Emit self thread name string.// 获取当前线程的名称std::string thread_name;self->GetThreadName(thread_name);ctx << thread_name;// Emit the wait time.// 记录当前线程等待锁的时间ctx << wait_ms;// 获取当前线程的代码位置(文件名、行号和方法名),并记录到日志中const char* filename = nullptr;int32_t line_number;std::string method_name;{uint32_t pc;ArtMethod* m = self->GetCurrentMethod(&pc);TranslateLocation(m, pc, &filename, &line_number);// Emit the source code file name.ctx << filename;// Emit the source code line number.ctx << line_number;// Emit the method name.method_name = ArtMethod::PrettyMethod(m);ctx << method_name;}// 记录锁持有者的代码位置(文件名、行号和方法名)。如果锁持有者的代码位置与当前线程相同,则使用 - 表示// 注意:持有者的文件名有可能为空// Emit the lock owner source code file name.if (owner_filename == nullptr) {owner_filename = "";} else if (strcmp(filename, owner_filename) == 0) {// Common case, so save on log space.owner_filename = "-";}ctx << owner_filename;// Emit the source code line number.ctx << owner_line_number;// Emit the owner method name.std::string owner_method_name = ArtMethod::PrettyMethod(owner_method);ctx << owner_method_name;// Emit the sample percentage.// 记录锁竞争事件的采样百分比ctx << sample_percent;// 记录日志类型为event日志ctx << LOG_ID_EVENTS;// Now report to other interested parties.// 将锁竞争事件的信息传递给其他组件PaletteReportLockContention(self->GetJniEnv(),wait_ms,filename,line_number,method_name.c_str(),owner_filename,owner_line_number,owner_method_name.c_str(),proc_name,thread_name.c_str());
}} // namespace art
举例说明
02-10 14:05:43.759 1000 2649 16342 I dvm_lock_sample: [
system_server,
1,
binder:2649_1D,
9865,
AccessibilityWindowManager.java,
1456,
void com.android.server.accessibility.AccessibilityWindowManager.removeAccessibilityInteractionConnection(android.view.IWindow),
-,
1472,
void com.android.server.accessibility.AccessibilityWindowManager.removeAccessibilityInteractionConnection(android.view.IWindow),
2733]
dvm_lock_sample日志的各个信息为:
- 进程:system_server
- 线程:binder:2649_1D
- 锁等待时间:9865ms
- 当前线程的代码位置:
- 文件:AccessibilityWindowManager.java
- 行号:1456
- 方法:void removeAccessibilityInteractionConnection(android.view.IWindow)
- 锁持有者的代码位置:
- 文件:AccessibilityWindowManager.java(与当前线程相同)
- 行号:1472
- 方法:void removeAccessibilityInteractionConnection(android.view.IWindow)
- 采样百分比:2733
从这条日志可以看出:
当前线程和锁持有者处于同一个文件和方法中。
锁等待时间较长(9865ms),这可能表明存在性能问题,尤其是在主线程(敏感线程)中。