??本文來分析下matrix
對于線程的監(jiān)控,matrix
對于線程的監(jiān)控主要 hook pthread 的pthread_create
pthread_detach
pthread_join
pthread_setname_np
幾個方法医增。
原理
??先來看下為什么 hook pthread 的幾個方法就可以監(jiān)控到線程头岔。
一般的 java 線程寫法
new Thread(new Runable{
@Override
void run(){
// do action
}
}).start();
public synchronized void start() {
...
started = false;
try {
//進入到 native 中
nativeCreate(this, stackSize, daemon);
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size,jboolean daemon) {
...
Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
if (VLOG_IS_ON(threads)) {
if (java_name != nullptr) {
thread_name = java_name->ToModifiedUtf8();
} else {
thread_name = "(Unnamed)";
}
}
Runtime* runtime = Runtime::Current();
// pthread 的回調(diào)參數(shù)
Thread* child_thread = new Thread(is_daemon);
// 設(shè)置 java 的 thread 類
child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer);
stack_size = FixStackSize(stack_size);
// 給 java thread 的 nativePeer 設(shè)置值為 child_thread 的地址
env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
reinterpret_cast<jlong>(child_thread));
std::string error_msg;
std::unique_ptr<JNIEnvExt> child_jni_env_ext(
JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM(), &error_msg));
int pthread_create_result = 0;
if (child_jni_env_ext.get() != nullptr) {
pthread_t new_pthread;
pthread_attr_t attr;
child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
// 創(chuàng)建 pthread ,回調(diào)函數(shù)為 Thread::CreateCallback 尤仍,回調(diào)函數(shù)的參數(shù)為 child_thread
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
}
}
void* Thread::CreateCallback(void* arg) {
Thread* self = reinterpret_cast<Thread*>(arg);
Runtime* runtime = Runtime::Current();
{
runtime->GetRuntimeCallbacks()->ThreadStart(self);
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
// 執(zhí)行 java thread run 方法
InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
}
// Detach and delete self.
Runtime::Current()->GetThreadList()->Unregister(self);
return nullptr;
}
所以可以通過 hook pthread 的一系列方法可以監(jiān)控到線程。
Matrix Thread Hook
主要的 hook 邏輯在 PthreadHook.cpp
中
- 開始 hook
void InstallHooks(bool enable_debug) {
FETCH_ORIGIN_FUNC(pthread_create)
// 替換
/**
* if (!orig_pthread_create) {
void *handle = dlopen("libc.so", RTLD_LAZY);
if (handle) {
orig_pthread_create = (fn_pthread_create_t)dlsym(handle, "pthread_create");
}
}
**/
// 同上轉(zhuǎn)換
FETCH_ORIGIN_FUNC(pthread_setname_np)
FETCH_ORIGIN_FUNC(pthread_detach)
FETCH_ORIGIN_FUNC(pthread_join)
if (sThreadTraceEnabled) {
thread_trace::thread_trace_init();
}
matrix::PauseLoadSo();
xhook_block_refresh();
{
int ret = xhook_export_symtable_hook("libc.so", "pthread_create",
(void *) HANDLER_FUNC_NAME(pthread_create),
nullptr);
LOGD(LOG_TAG, "export table hook sym: pthread_create, ret: %d", ret);
ret = xhook_export_symtable_hook("libc.so", "pthread_setname_np",
(void *) HANDLER_FUNC_NAME(pthread_setname_np),
nullptr);
LOGD(LOG_TAG, "export table hook sym: pthread_setname_np, ret: %d", ret);
xhook_grouped_register(HOOK_REQUEST_GROUPID_PTHREAD, ".*/.*\\.so$", "pthread_create",
(void *) HANDLER_FUNC_NAME(pthread_create), nullptr);
xhook_grouped_register(HOOK_REQUEST_GROUPID_PTHREAD, ".*/.*\\.so$",
"pthread_setname_np",
(void *) HANDLER_FUNC_NAME(pthread_setname_np), nullptr);
xhook_grouped_register(HOOK_REQUEST_GROUPID_PTHREAD, ".*/.*\\.so$", "pthread_detach",
(void *) HANDLER_FUNC_NAME(pthread_detach), nullptr);
xhook_grouped_register(HOOK_REQUEST_GROUPID_PTHREAD, ".*/.*\\.so$", "pthread_join",
(void *) HANDLER_FUNC_NAME(pthread_join), nullptr);
ret = xhook_export_symtable_hook("libc.so", "pthread_detach",
(void *) HANDLER_FUNC_NAME(pthread_detach), nullptr);
LOGD(LOG_TAG, "export table hook sym: pthread_detach, ret: %d", ret);
ret = xhook_export_symtable_hook("libc.so", "pthread_join",
(void *) HANDLER_FUNC_NAME(pthread_join), nullptr);
LOGD(LOG_TAG, "export table hook sym: pthread_join, ret: %d", ret);
xhook_enable_debug(enable_debug ? 1 : 0);
xhook_enable_sigsegv_protection(0);
xhook_refresh(0);
}
xhook_unblock_refresh();
matrix::ResumeLoadSo();
}
-
說明
宏定義在 matrix-commons Model 中的 HookCommon.h 中
// FETCH_ORIGIN_FUNC(pthread_create)
// 是一個宏定義,需要替換還原出代碼
// 對應(yīng)的宏
#define HANDLER_FUNC_NAME(fn_name) h_##fn_name
#define ORIGINAL_FUNC_NAME(fn_name) orig_##fn_name
#define RTLD_LAZY 0x00001
#define FUNC_TYPE(sym) fn_##sym##_t
#define FETCH_ORIGIN_FUNC(sym) \
if (!ORIGINAL_FUNC_NAME(sym)) { \
void *handle = dlopen(ORIGINAL_LIB, RTLD_LAZY); \
if (handle) { \
ORIGINAL_FUNC_NAME(sym) = (FUNC_TYPE(sym))dlsym(handle, #sym); \
} \
}
// 替換后代碼
if (!orig_pthread_create) {
void *handle = dlopen("libc.so", RTLD_LAZY);
if (handle) {
orig_pthread_create = (fn_pthread_create_t)dlsym(handle, "pthread_create");
}
}
DECLARE_HOOK_ORIG(int, pthread_create, pthread_t*, pthread_attr_t const*,
pthread_hook::pthread_routine_t, void*);
// 替換后
typedef int (*fn_pthread_create_t)(pthread_t*, pthread_attr_t const*,pthread_hook::pthread_routine_t, void*);
extern fn_pthread_create_t orig_pthread_create;
int h_pthread_create(pthread_t*, pthread_attr_t const*,pthread_hook::pthread_routine_t, void*);
// 再看這句
int ret = xhook_export_symtable_hook("libc.so", "pthread_create",(void *) HANDLER_FUNC_NAME(pthread_create),nullptr);
// 替換后
int ret = xhook_export_symtable_hook("libc.so", "pthread_create",(void *) h_pthread_create,nullptr);
// 而 h_pthread_create 這個函數(shù)也是一個宏定義 對于為
DEFINE_HOOK_FUN(int, pthread_create,
pthread_t *pthread, pthread_attr_t const *attr,
pthread_hook::pthread_routine_t start_routine, void *args) {...}
// 替換后
fn_pthread_create_t orig_pthread_create;
int h_pthread_create(pthread_t *pthread, pthread_attr_t const *attr,pthread_hook::pthread_routine_t start_routine, void *args){...}
其余的宏也如此税朴。
- pthread_create hook
對于 pthread_create 方法,hook 后會執(zhí)行 h_pthread_create 方法
fn_pthread_create_t orig_pthread_create;
int h_pthread_create(pthread_t *pthread, pthread_attr_t const *attr,pthread_hook::pthread_routine_t start_routine, void *args){
Dl_info callerInfo = {};
bool callerInfoOk = true;
if (dladdr(__builtin_return_address(0), &callerInfo) == 0) {
LOGE(LOG_TAG, "%d >> Fail to get caller info.", ::getpid());
callerInfoOk = false;
}
pthread_attr_t tmpAttr;
if (LIKELY(attr == nullptr)) {
...
} else {
tmpAttr = *attr;
}
int ret = 0;
if (sThreadTraceEnabled) {
auto *routine_wrapper = thread_trace::wrap_pthread_routine(start_routine, args);
// 執(zhí)行原函數(shù)
CALL_ORIGIN_FUNC_RET(int, tmpRet, pthread_create, pthread, &tmpAttr,
routine_wrapper->wrapped_func,
routine_wrapper);
/**
*if(!orig_pthread_create){
* void *handle = dlopen("libc.so", 0x00001);
* if (handle) {
* orig_pthread_create = (fn_pthread_create_t)dlsym(handle, #pthread_create);
* }
*}
* int tmpRet = orig_pthread_create(pthread, &tmpAttr,routine_wrapper->wrapped_func,routine_wrapper);
**/
ret = tmpRet;
} else {
...
}
// 收集信息
if (LIKELY(ret == 0) && sThreadTraceEnabled) {
thread_trace::handle_pthread_create(*pthread);
}
if (LIKELY(attr == nullptr)) {
pthread_attr_destroy(&tmpAttr);
}
return ret;
}
// notice: 在父線程回調(diào)此函數(shù)
void thread_trace::handle_pthread_create(const pthread_t pthread) {
const char *arch =
#ifdef __aarch64__
"aarch64";
#elif defined __arm__
"arm";
#endif
LOGD(TAG, "+++++++ on_pthread_create, %s", arch);
pid_t tid = pthread_gettid_np(pthread);
...
if (!m_quicken_unwind) {
const size_t BUF_SIZE = 1024;
char *java_stacktrace = static_cast<char *>(malloc(BUF_SIZE));
strncpy(java_stacktrace, "(init stacktrace)", BUF_SIZE);
if (m_java_stacktrace_mutex.try_lock_for(std::chrono::milliseconds(100))) {
if (java_stacktrace) {
// 獲取 java 堆棧
get_java_stacktrace(java_stacktrace, BUF_SIZE);
}
m_java_stacktrace_mutex.unlock();
} else {
LOGE(TAG, "maybe reentrant!");
}
LOGD(TAG, "parent_tid: %d -> tid: %d", pthread_gettid_np(pthread_self()), tid);
bool recorded = on_pthread_create_locked(pthread, java_stacktrace, false, tid);
if (!recorded && java_stacktrace) {
free(java_stacktrace);
}
} else {
...
}
//
rp_release();
notify_routine(pthread);
LOGD(TAG, "------ on_pthread_create end");
}
//static std::map<pthread_t, pthread_meta_t> m_pthread_metas;
static inline bool
on_pthread_create_locked(const pthread_t pthread, char *java_stacktrace, bool quicken_unwind,
pid_t tid) {
pthread_meta_lock meta_lock(m_pthread_meta_mutex);
// always false
if (m_pthread_metas.count(pthread)) {
LOGD(TAG, "on_pthread_create: thread already recorded");
return false;
}
// 從 m_pthread_metas 取出 key=pthread 的 pthread_meta_t家制,沒有就創(chuàng)建一個并添加到 m_pthread_metas 中
pthread_meta_t &meta = m_pthread_metas[pthread];
meta.tid = tid;
// 如果還沒 setname, 此時拿到的是父線程的名字, 在 setname 的時候有一次更正機會, 否則繼承父線程名字
// 如果已經(jīng) setname, 那么此時拿到的就是當(dāng)前創(chuàng)建線程的名字
meta.thread_name = static_cast<char *>(malloc(sizeof(char) * THREAD_NAME_LEN));
if (0 != pthread_getname_ext(pthread, meta.thread_name, THREAD_NAME_LEN)) {
char temp_name[THREAD_NAME_LEN];
snprintf(temp_name, THREAD_NAME_LEN, "tid-%d", pthread_gettid_np(pthread));
strncpy(meta.thread_name, temp_name, THREAD_NAME_LEN);
}
LOGD(TAG, "on_pthread_create: pthread = %ld, thread name: %s, %llu", pthread, meta.thread_name,
(uint64_t) tid);
// 將線程名字匹配正則表達(dá)式 ".*" 正林,成功就加入
if (test_match_thread_name(meta)) {
m_filtered_pthreads.insert(pthread);
}
uint64_t native_hash = 0;
uint64_t java_hash = 0;
// 利用 wechat_backtrace 獲取 native 堆棧
if (quicken_unwind) {
meta.unwind_mode = wechat_backtrace::Quicken;
wechat_backtrace::quicken_based_unwind(meta.native_backtrace.frames.get(),
meta.native_backtrace.max_frames,
meta.native_backtrace.frame_size);
} else {
meta.unwind_mode = wechat_backtrace::get_backtrace_mode();
wechat_backtrace::unwind_adapter(meta.native_backtrace.frames.get(),
meta.native_backtrace.max_frames,
meta.native_backtrace.frame_size);
}
native_hash = hash_backtrace_frames(&(meta.native_backtrace));
if (java_stacktrace) {
meta.java_stacktrace.store(java_stacktrace);
java_hash = hash_str(java_stacktrace);
LOGD(TAG, "on_pthread_create: java hash = %llu", (wechat_backtrace::ullint_t) java_hash);
}
LOGD(TAG, "on_pthread_create: pthread = %ld, thread name: %s end.", pthread, meta.thread_name);
// 合并
if (native_hash || java_hash) {
meta.hash = hash_combine(native_hash, java_hash);
}
return true;
}
??總的來說有兩點,一是在 m_pthread_metas 這個 map 中記錄 pthread_t 為 key , pthread_meta_t 為 value 的信息颤殴,pthread_meta_t 可記錄 java/native 堆棧 觅廓、tid、thread_name 及 hash 信息涵但。二是將符合正則表達(dá)式(".*")的 metas.thread_name 記錄到 m_filtered_pthreads(set) 中杈绸。
- pthread_setname_np hook
ret = xhook_export_symtable_hook("libc.so", "pthread_setname_np",
(void *) HANDLER_FUNC_NAME(pthread_setname_np),//h_pthread_setname_np
nullptr);
DEFINE_HOOK_FUN(int, pthread_setname_np, pthread_t pthread, const char *name) {
CALL_ORIGIN_FUNC_RET(int, ret, pthread_setname_np, pthread, name);
if (LIKELY(ret == 0) && sThreadTraceEnabled) {
thread_trace::handle_pthread_setname_np(pthread, name);
}
return ret;
}
/**
* 設(shè)置線程的名字
fn_pthread_setname_np_t orig_pthread_setname_np;
int h_pthread_setname_np(pthread_t pthread, const char *name){
if (!orig_pthread_setname_np) {
void *handle = dlopen("libc.so", 0x01);
if (handle) {
orig_pthread_setname_np = (fn_pthread_setname_np_t)dlsym(handle, pthread_setname_np);
}
}
if (LIKELY(ret == 0) && sThreadTraceEnabled) {
thread_trace::handle_pthread_setname_np(pthread, name);
}
return ret;
}
**/
/**
* ~~handle_pthread_setname_np 有可能在 handle_pthread_create 之前先執(zhí)行~~
* 在增加了 cond 之后, 必然后于 on_pthread_create 執(zhí)行
*
* @param pthread
* @param name
*/
void thread_trace::handle_pthread_setname_np(pthread_t pthread, const char *name) {
if (NULL == name) {
LOGE(TAG, "setting name null");
return;
}
const size_t name_len = strlen(name);
if (0 == name_len || name_len >= THREAD_NAME_LEN) {
LOGE(TAG, "pthread name is illegal, just ignore. len(%s)", name);
return;
}
LOGD(TAG, "++++++++ pre handle_pthread_setname_np tid: %d, %s", pthread_gettid_np(pthread),
name);
{
pthread_meta_lock meta_lock(m_pthread_meta_mutex);
if (!m_pthread_metas.count(pthread)) { // always false
// 到這里說明沒有回調(diào) on_pthread_create, setname 對 on_pthread_create 是可見的
auto lost_thread_name = static_cast<char *>(malloc(sizeof(char) * THREAD_NAME_LEN));
// 拿到 pthread 的名字
pthread_getname_ext(pthread, lost_thread_name, THREAD_NAME_LEN);
LOGE(TAG,
"handle_pthread_setname_np: pthread hook lost: {%s} -> {%s}, maybe on_create has not been called",
lost_thread_name, name);
free(lost_thread_name);
return;
}
// 到這里說明 on_pthread_create 已經(jīng)回調(diào)了, 需要修正并檢查新的線程名是否 match 正則
pthread_meta_t &meta = m_pthread_metas.at(pthread);
// 設(shè)置名字
strncpy(meta.thread_name, name, THREAD_NAME_LEN);
bool parent_match = m_filtered_pthreads.count(pthread) != 0;
// 如果新線程名不 match, 但父線程名 match, 說明需要從 filter 集合中移除
if (!test_match_thread_name(meta) && parent_match) {
m_filtered_pthreads.erase(pthread);
LOGD(TAG, "--------------------------");
return;
}
// 如果新線程 match, 但父線程名不 match, 說明需要添加僅 filter 集合
if (test_match_thread_name(meta) && !parent_match) {
m_filtered_pthreads.insert(pthread);
LOGD(TAG, "--------------------------");
return;
}
}
// 否則, 啥也不干 (都 match, 都不 match)
LOGD(TAG, "--------------------------");
}
??找到了 m_pthread_metas 中的 pthread_meta_t,對其修改了名字贤笆,最后根據(jù)新改的名字是否匹配正則(".*") 和父線程名是否匹配蝇棉,對 m_filtered_pthreads 進行了刪除和增加。
- pthread_detach 和 pthread_join hook hook
DEFINE_HOOK_FUN(int, pthread_detach, pthread_t pthread) {
CALL_ORIGIN_FUNC_RET(int, ret, pthread_detach, pthread);
LOGD(LOG_TAG, "pthread_detach : %d", ret);
if (LIKELY(ret == 0) && sThreadTraceEnabled) {
thread_trace::handle_pthread_release(pthread);
}
return ret;
}
DEFINE_HOOK_FUN(int, pthread_join, pthread_t pthread, void **return_value_ptr) {
CALL_ORIGIN_FUNC_RET(int, ret, pthread_join, pthread, return_value_ptr);
LOGD(LOG_TAG, "pthread_join : %d", ret);
if (LIKELY(ret == 0) && sThreadTraceEnabled) {
thread_trace::handle_pthread_release(pthread);
}
return ret;
}
void thread_trace::handle_pthread_release(pthread_t pthread) {
LOGD(TAG, "handle_pthread_release");
if (!m_trace_pthread_release) {
LOGD(TAG, "handle_pthread_release disabled");
return;
}
on_pthread_release(pthread);
}
static void on_pthread_release(pthread_t pthread) {
LOGD(TAG, "on_pthread_release");
pthread_meta_lock meta_lock(m_pthread_meta_mutex);
if (!m_pthread_metas.count(pthread)) {
LOGD(TAG, "on_pthread_exit: thread not found");
return;
}
erase_meta(m_pthread_metas, pthread, m_pthread_metas.at(pthread));
LOGD(TAG, "on_pthread_release end");
}
static void erase_meta(std::map<pthread_t, pthread_meta_t> &metas, pthread_t &pthread, pthread_meta_t &meta) {
free(meta.thread_name);
char *java_stacktrace = meta.java_stacktrace.load(std::memory_order_acquire);
if (java_stacktrace) {
free(java_stacktrace);
}
m_pthread_metas.erase(pthread);
}
??在 m_pthread_metas 刪除了 key=pthread 的數(shù)據(jù)芥永。
dump pthread json_file
static inline void pthread_dump_json_impl(FILE *log_file) {//log_file 需要記錄到該文件中
LOGD(TAG, "pthread dump waiting count: %zu", m_pthread_routine_flags.size());
std::map<uint64_t, std::vector<pthread_meta_t>> pthread_metas_not_exited;
for (auto &i : m_filtered_pthreads) {
auto &meta = m_pthread_metas[i];
if (meta.hash) {
auto &hash_bucket = pthread_metas_not_exited[meta.hash];
hash_bucket.emplace_back(meta);
}
}
std::map<uint64_t, std::vector<pthread_meta_t>> pthread_metas_not_released;
for (auto &i : m_pthread_metas) {
auto &meta = i.second;
if (!meta.exited) { //一般為 false
continue;
}
if (meta.hash) {
auto &hash_bucket = pthread_metas_not_released[meta.hash];
hash_bucket.emplace_back(meta);
}
}
char *json_str = NULL;
cJSON *json_array_threads_not_exited = NULL;
cJSON *json_array_threads_not_released = NULL;
bool success = false;
cJSON *json_obj = cJSON_CreateObject();
if (!json_obj) {
goto err;
}
json_array_threads_not_exited = cJSON_AddArrayToObject(json_obj, "PthreadHook_not_exited");
json_array_threads_not_released = cJSON_AddArrayToObject(json_obj, "PthreadHook_not_released");
if (!json_array_threads_not_exited || !json_array_threads_not_released) {
goto err;
}
// 將 pthread_metas_not_exited 的內(nèi)容加入到 json_array_threads_not_exited 中
success &= append_meta_2_json_array(json_array_threads_not_exited, pthread_metas_not_exited);
success &= append_meta_2_json_array(json_array_threads_not_released, pthread_metas_not_released);
json_str = cJSON_PrintUnformatted(json_obj);
cJSON_Delete(json_obj);
flogger0(log_file, "%s", json_str);
cJSON_free(json_str);
return;
err:
LOGD(TAG, "ERROR: create cJSON object failed");
cJSON_Delete(json_obj);
return;
}
??將 m_filtered_pthreads 中記錄的數(shù)據(jù)和 m_pthread_metas 中 metas.exited=true 的數(shù)據(jù)輸出到一個 json_log 文件中篡殷。文件類似與
{
"PthreadHook_not_exited":
[
{
"hash": "614404817278743815",
"native": "#pc cda00 (null) (/data/app/com.tencent.matrix.test.memoryhook-ns8hChwbmXvfAOKF9wkrHw==/lib/arm64/libwechatbacktrace.so);",
"java": "dalvik.system.VMStack.getThreadStackTrace(Native Method);com.tencent.matrix.hook.HookManager.getStack(HookManager.java:163);",
"count": "2",
"threads":
[
{
"tid": "6673",
"name": "Test"
},
{
"tid": "5962",
"name": "Test"
}
]
}
],
"PthreadHook_not_released":
[]
}