From 4c0e96592d5f5f884aab9b29a3300ae5e1b6009b Mon Sep 17 00:00:00 2001 From: robaho Date: Sun, 28 Feb 2021 18:37:44 +0100 Subject: darwin: scan thread information Inspired by: https://github.com/hishamhm/htop/pull/848 Closes: #542 --- darwin/DarwinProcess.c | 128 +++++++++++++++++++++++++++++++++----------- darwin/DarwinProcess.h | 2 +- darwin/DarwinProcessTable.c | 2 +- 3 files changed, 99 insertions(+), 33 deletions(-) diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index 1da4221c..717e467f 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -404,12 +404,28 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable } } +static ProcessState stateToChar(int run_state) { + switch (run_state) { + case TH_STATE_RUNNING: + return RUNNING; + case TH_STATE_STOPPED: + return STOPPED; + case TH_STATE_WAITING: + return WAITING; + case TH_STATE_UNINTERRUPTIBLE: + return UNINTERRUPTIBLE_WAIT; + case TH_STATE_HALTED: + return BLOCKED; + } + return UNKNOWN; +} + /* * Scan threads for process state information. * Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread * and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c */ -void DarwinProcess_scanThreads(DarwinProcess* dp) { +void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt) { Process* proc = (Process*) dp; kern_return_t ret; @@ -421,55 +437,105 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) { return; } - task_t port; - ret = task_for_pid(mach_task_self(), Process_getPid(proc), &port); + pid_t pid = Process_getPid(proc); + + task_t task; + ret = task_for_pid(mach_task_self(), pid, &task); if (ret != KERN_SUCCESS) { + // TODO: workaround for modern MacOS limits on task_for_pid() + if (ret != KERN_FAILURE) + CRT_debug("task_for_pid(%d) failed: %s", pid, mach_error_string(ret)); dp->taskAccess = false; return; } - task_info_data_t tinfo; - mach_msg_type_number_t task_info_count = TASK_INFO_MAX; - ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count); - if (ret != KERN_SUCCESS) { - dp->taskAccess = false; - return; + { + task_info_data_t tinfo; + mach_msg_type_number_t task_info_count = TASK_INFO_MAX; + ret = task_info(task, TASK_BASIC_INFO, (task_info_t) &tinfo, &task_info_count); + if (ret != KERN_SUCCESS) { + CRT_debug("task_info(%d) failed: %s", pid, mach_error_string(ret)); + dp->taskAccess = false; + mach_port_deallocate(mach_task_self(), task); + return; + } } thread_array_t thread_list; mach_msg_type_number_t thread_count; - ret = task_threads(port, &thread_list, &thread_count); + ret = task_threads(task, &thread_list, &thread_count); if (ret != KERN_SUCCESS) { + CRT_debug("task_threads(%d) failed: %s", pid, mach_error_string(ret)); dp->taskAccess = false; - mach_port_deallocate(mach_task_self(), port); + mach_port_deallocate(mach_task_self(), task); return; } + const bool hideUserlandThreads = dpt->super.super.host->settings->hideUserlandThreads; + integer_t run_state = 999; - for (unsigned int i = 0; i < thread_count; i++) { - thread_info_data_t thinfo; - mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT; - ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count); - if (ret == KERN_SUCCESS) { - thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo; - if (basic_info_th->run_state < run_state) { - run_state = basic_info_th->run_state; - } - mach_port_deallocate(mach_task_self(), thread_list[i]); + for (mach_msg_type_number_t i = 0; i < thread_count; i++) { + + thread_identifier_info_data_t identifer_info; + mach_msg_type_number_t identifer_info_count = THREAD_IDENTIFIER_INFO_COUNT; + ret = thread_info(thread_list[i], THREAD_IDENTIFIER_INFO, (thread_info_t) &identifer_info, &identifer_info_count); + if (ret != KERN_SUCCESS) { + CRT_debug("thread_info(%d:%d) for identifier failed: %s", pid, i, mach_error_string(ret)); + continue; + } + + uint64_t tid = identifer_info.thread_id; + + bool preExisting; + Process *tprocess = ProcessTable_getProcess(&dpt->super, tid, &preExisting, DarwinProcess_new); + tprocess->super.updated = true; + dpt->super.totalTasks++; + + if (hideUserlandThreads) { + tprocess->super.show = false; + continue; } + + assert(Process_getPid(tprocess) == tid); + Process_setParent(tprocess, pid); + Process_setThreadGroup(tprocess, pid); + tprocess->super.show = true; + tprocess->isUserlandThread = true; + tprocess->st_uid = proc->st_uid; + tprocess->user = proc->user; + + thread_extended_info_data_t extended_info; + mach_msg_type_number_t extended_info_count = THREAD_EXTENDED_INFO_COUNT; + ret = thread_info(thread_list[i], THREAD_EXTENDED_INFO, (thread_info_t) &extended_info, &extended_info_count); + if (ret != KERN_SUCCESS) { + CRT_debug("thread_info(%d:%d) for extended failed: %s", pid, i, mach_error_string(ret)); + continue; + } + + DarwinProcess* tdproc = (DarwinProcess*)tprocess; + tdproc->super.state = stateToChar(extended_info.pth_run_state); + tdproc->super.percent_cpu = extended_info.pth_cpu_usage / 10.0; + tdproc->stime = extended_info.pth_system_time; + tdproc->utime = extended_info.pth_user_time; + tdproc->super.time = (extended_info.pth_system_time + extended_info.pth_user_time) / 10000000; + tdproc->super.priority = extended_info.pth_curpri; + + if (extended_info.pth_run_state < run_state) + run_state = extended_info.pth_run_state; + + // TODO: depend on setting + const char* name = extended_info.pth_name[0] != '\0' ? extended_info.pth_name : proc->procComm; + Process_updateCmdline(tprocess, name, 0, strlen(name)); + + if (!preExisting) + ProcessTable_add(&dpt->super, tprocess); } + vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count); - mach_port_deallocate(mach_task_self(), port); + mach_port_deallocate(mach_task_self(), task); - /* Taken from: https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/thread_info.h#L129 */ - switch (run_state) { - case TH_STATE_RUNNING: proc->state = RUNNING; break; - case TH_STATE_STOPPED: proc->state = STOPPED; break; - case TH_STATE_WAITING: proc->state = WAITING; break; - case TH_STATE_UNINTERRUPTIBLE: proc->state = UNINTERRUPTIBLE_WAIT; break; - case TH_STATE_HALTED: proc->state = BLOCKED; break; - default: proc->state = UNKNOWN; - } + if (run_state != 999) + proc->state = stateToChar(run_state); } diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h index 496b179b..a5d66b49 100644 --- a/darwin/DarwinProcess.h +++ b/darwin/DarwinProcess.h @@ -41,6 +41,6 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable * Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread * and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c */ -void DarwinProcess_scanThreads(DarwinProcess* dp); +void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt); #endif diff --git a/darwin/DarwinProcessTable.c b/darwin/DarwinProcessTable.c index 850b503f..a08c2669 100644 --- a/darwin/DarwinProcessTable.c +++ b/darwin/DarwinProcessTable.c @@ -112,7 +112,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0}); if (isScanThreadSupported) { - DarwinProcess_scanThreads(proc); + DarwinProcess_scanThreads(proc, dpt); } super->totalTasks += 1; -- cgit v1.2.3