Skip to content

Commit 423d183

Browse files
robahoBenBE
authored andcommitted
[WIP] Darwin: scan thread information
Inspired by: hishamhm/htop#848 Closes: #542
1 parent 0c00275 commit 423d183

File tree

3 files changed

+99
-33
lines changed

3 files changed

+99
-33
lines changed

darwin/DarwinProcess.c

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,28 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable
404404
}
405405
}
406406

407+
static ProcessState stateToChar(int run_state) {
408+
switch (run_state) {
409+
case TH_STATE_RUNNING:
410+
return RUNNING;
411+
case TH_STATE_STOPPED:
412+
return STOPPED;
413+
case TH_STATE_WAITING:
414+
return WAITING;
415+
case TH_STATE_UNINTERRUPTIBLE:
416+
return UNINTERRUPTIBLE_WAIT;
417+
case TH_STATE_HALTED:
418+
return BLOCKED;
419+
}
420+
return UNKNOWN;
421+
}
422+
407423
/*
408424
* Scan threads for process state information.
409425
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
410426
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
411427
*/
412-
void DarwinProcess_scanThreads(DarwinProcess* dp) {
428+
void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt) {
413429
Process* proc = (Process*) dp;
414430
kern_return_t ret;
415431

@@ -421,55 +437,105 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
421437
return;
422438
}
423439

424-
task_t port;
425-
ret = task_for_pid(mach_task_self(), Process_getPid(proc), &port);
440+
pid_t pid = Process_getPid(proc);
441+
442+
task_t task;
443+
ret = task_for_pid(mach_task_self(), pid, &task);
426444
if (ret != KERN_SUCCESS) {
445+
// TODO: workaround for modern MacOS limits on task_for_pid()
446+
if (ret != KERN_FAILURE)
447+
CRT_debug("task_for_pid(%d) failed: %s", pid, mach_error_string(ret));
427448
dp->taskAccess = false;
428449
return;
429450
}
430451

431-
task_info_data_t tinfo;
432-
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
433-
ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count);
434-
if (ret != KERN_SUCCESS) {
435-
dp->taskAccess = false;
436-
return;
452+
{
453+
task_info_data_t tinfo;
454+
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
455+
ret = task_info(task, TASK_BASIC_INFO, (task_info_t) &tinfo, &task_info_count);
456+
if (ret != KERN_SUCCESS) {
457+
CRT_debug("task_info(%d) failed: %s", pid, mach_error_string(ret));
458+
dp->taskAccess = false;
459+
mach_port_deallocate(mach_task_self(), task);
460+
return;
461+
}
437462
}
438463

439464
thread_array_t thread_list;
440465
mach_msg_type_number_t thread_count;
441-
ret = task_threads(port, &thread_list, &thread_count);
466+
ret = task_threads(task, &thread_list, &thread_count);
442467
if (ret != KERN_SUCCESS) {
468+
CRT_debug("task_threads(%d) failed: %s", pid, mach_error_string(ret));
443469
dp->taskAccess = false;
444-
mach_port_deallocate(mach_task_self(), port);
470+
mach_port_deallocate(mach_task_self(), task);
445471
return;
446472
}
447473

474+
const bool hideUserlandThreads = dpt->super.super.host->settings->hideUserlandThreads;
475+
448476
integer_t run_state = 999;
449-
for (unsigned int i = 0; i < thread_count; i++) {
450-
thread_info_data_t thinfo;
451-
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
452-
ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
453-
if (ret == KERN_SUCCESS) {
454-
thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo;
455-
if (basic_info_th->run_state < run_state) {
456-
run_state = basic_info_th->run_state;
457-
}
458-
mach_port_deallocate(mach_task_self(), thread_list[i]);
477+
for (mach_msg_type_number_t i = 0; i < thread_count; i++) {
478+
479+
thread_identifier_info_data_t identifer_info;
480+
mach_msg_type_number_t identifer_info_count = THREAD_IDENTIFIER_INFO_COUNT;
481+
ret = thread_info(thread_list[i], THREAD_IDENTIFIER_INFO, (thread_info_t) &identifer_info, &identifer_info_count);
482+
if (ret != KERN_SUCCESS) {
483+
CRT_debug("thread_info(%d:%d) for identifier failed: %s", pid, i, mach_error_string(ret));
484+
continue;
485+
}
486+
487+
uint64_t tid = identifer_info.thread_id;
488+
489+
bool preExisting;
490+
Process *tprocess = ProcessTable_getProcess(&dpt->super, tid, &preExisting, DarwinProcess_new);
491+
tprocess->super.updated = true;
492+
dpt->super.totalTasks++;
493+
494+
if (hideUserlandThreads) {
495+
tprocess->super.show = false;
496+
continue;
459497
}
498+
499+
assert(Process_getPid(tprocess) == tid);
500+
Process_setParent(tprocess, pid);
501+
Process_setThreadGroup(tprocess, pid);
502+
tprocess->super.show = true;
503+
tprocess->isUserlandThread = true;
504+
tprocess->st_uid = proc->st_uid;
505+
tprocess->user = proc->user;
506+
507+
thread_extended_info_data_t extended_info;
508+
mach_msg_type_number_t extended_info_count = THREAD_EXTENDED_INFO_COUNT;
509+
ret = thread_info(thread_list[i], THREAD_EXTENDED_INFO, (thread_info_t) &extended_info, &extended_info_count);
510+
if (ret != KERN_SUCCESS) {
511+
CRT_debug("thread_info(%d:%d) for extended failed: %s", pid, i, mach_error_string(ret));
512+
continue;
513+
}
514+
515+
DarwinProcess* tdproc = (DarwinProcess*)tprocess;
516+
tdproc->super.state = stateToChar(extended_info.pth_run_state);
517+
tdproc->super.percent_cpu = extended_info.pth_cpu_usage / 10.0;
518+
tdproc->stime = extended_info.pth_system_time;
519+
tdproc->utime = extended_info.pth_user_time;
520+
tdproc->super.time = (extended_info.pth_system_time + extended_info.pth_user_time) / 10000000;
521+
tdproc->super.priority = extended_info.pth_curpri;
522+
523+
if (extended_info.pth_run_state < run_state)
524+
run_state = extended_info.pth_run_state;
525+
526+
// TODO: depend on setting
527+
const char* name = extended_info.pth_name[0] != '\0' ? extended_info.pth_name : proc->procComm;
528+
Process_updateCmdline(tprocess, name, 0, strlen(name));
529+
530+
if (!preExisting)
531+
ProcessTable_add(&dpt->super, tprocess);
460532
}
533+
461534
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
462-
mach_port_deallocate(mach_task_self(), port);
535+
mach_port_deallocate(mach_task_self(), task);
463536

464-
/* Taken from: https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/thread_info.h#L129 */
465-
switch (run_state) {
466-
case TH_STATE_RUNNING: proc->state = RUNNING; break;
467-
case TH_STATE_STOPPED: proc->state = STOPPED; break;
468-
case TH_STATE_WAITING: proc->state = WAITING; break;
469-
case TH_STATE_UNINTERRUPTIBLE: proc->state = UNINTERRUPTIBLE_WAIT; break;
470-
case TH_STATE_HALTED: proc->state = BLOCKED; break;
471-
default: proc->state = UNKNOWN;
472-
}
537+
if (run_state != 999)
538+
proc->state = stateToChar(run_state);
473539
}
474540

475541

darwin/DarwinProcess.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable
4141
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
4242
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
4343
*/
44-
void DarwinProcess_scanThreads(DarwinProcess* dp);
44+
void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt);
4545

4646
#endif

darwin/DarwinProcessTable.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ void ProcessTable_goThroughEntries(ProcessTable* super) {
112112
bool isScanThreadSupported = !Platform_KernelVersionIsBetween((KernelVersion) {17, 0, 0}, (KernelVersion) {17, 5, 0});
113113

114114
if (isScanThreadSupported) {
115-
DarwinProcess_scanThreads(proc);
115+
DarwinProcess_scanThreads(proc, dpt);
116116
}
117117

118118
super->totalTasks += 1;

0 commit comments

Comments
 (0)