File: get_process_info_mac.c

package info (click to toggle)
nvtop 3.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,292 kB
  • sloc: ansic: 13,121; objc: 181; cpp: 156; sh: 70; makefile: 3
file content (137 lines) | stat: -rw-r--r-- 3,993 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 *
 * Copyright (C) 2023 Robin Voetter <robin@voetter.nl>
 *
 * This file is part of Nvtop.
 *
 * Nvtop is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Nvtop is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with nvtop.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "nvtop/get_process_info.h"

#include <libproc.h>
#include <sys/sysctl.h>
#include <pwd.h>
#include <mach/mach_time.h>

#include <string.h>
#include <stdio.h>

void get_username_from_pid(pid_t pid, char **buffer) {
  struct proc_bsdshortinfo proc;
  const int st = proc_pidinfo(pid, PROC_PIDT_SHORTBSDINFO, 0, &proc, PROC_PIDT_SHORTBSDINFO_SIZE);
  if (st != PROC_PIDT_SHORTBSDINFO_SIZE) {
    goto error;
  }

  struct passwd *user_info = getpwuid(proc.pbsi_uid);
  if (user_info == NULL) {
    goto error;
  }

  const size_t namelen = strlen(user_info->pw_name) + 1;
  *buffer = malloc(namelen * sizeof(**buffer));
  strncpy(*buffer, user_info->pw_name, namelen);
  return;
error:
  *buffer = NULL;
}

void get_command_from_pid(pid_t pid, char **buffer) {
  // See https://chromium.googlesource.com/crashpad/crashpad/+/360e441c53ab4191a6fd2472cc57c3343a2f6944/util/posix/process_util_mac.cc
  size_t argmax;
  size_t argmax_estimate;
  char *procargs = NULL;
  int tries = 3;
  do {
    int mib[] = {CTL_KERN, KERN_PROCARGS2, pid};
    if (sysctl(mib, 3, NULL, &argmax_estimate, NULL, 0) != 0) {
      goto error_free_procargs;
    }

    argmax = argmax_estimate + 1;
    procargs = realloc(procargs, argmax);
    if (sysctl(mib, 3, procargs, &argmax, NULL, 0) != 0) {
      goto error_free_procargs;
    }
  } while (argmax == argmax_estimate + 1 && --tries != 0);

  unsigned argc;
  memcpy(&argc, procargs, sizeof(argc));

  size_t i = sizeof(argc);
  // Skip executable path.
  while (i < argmax && procargs[i] != 0) {
    ++i;
  }
  // Find the first string
  while (i < argmax && procargs[i] == 0) {
    ++i;
  }

  const size_t argv0 = i;
  // Count the total size of the args by finding the end.
  for (unsigned int arg = 0; arg < argc && i < argmax; ++arg) {
    while (i < argmax && procargs[i] != 0) {
      ++i;
    }
    ++i; // We are going to replace this null character with a space (or a null in the case of the last).
  }
  const size_t args_size = i - argv0;
  if (args_size == 0) {
    goto error_free_procargs;
  }
  char* args = malloc(args_size);
  *buffer = args;

  i = argv0;
  for (unsigned int arg = 0; arg < argc && i < argmax; ++arg) {
    while (i < argmax && procargs[i] != 0) {
      *args++ = procargs[i++];
    }
    *args++ = ' ';
    ++i;
  }
  args[-1] = 0;

  free(procargs);
  return;
error_free_procargs:
  free(procargs);
  *buffer = NULL;
  return;
}

bool get_process_info(pid_t pid, struct process_cpu_usage *usage) {
  struct proc_taskinfo proc;
  const int st = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &proc, PROC_PIDTASKINFO_SIZE);
  if (st != PROC_PIDTASKINFO_SIZE) {
    return false;
  }

  nvtop_get_current_time(&usage->timestamp);

  // TODO: Should we implement this workaround?
  // https://github.com/htop-dev/htop/blob/main/darwin/PlatformHelpers.c#L98
  mach_timebase_info_data_t info;
  mach_timebase_info(&info);
  const double nanoseconds_per_tick = (double)info.numer / (double)info.denom;

  usage->total_user_time = (proc.pti_total_user * nanoseconds_per_tick) / 1000000000.0;
  usage->total_kernel_time = (proc.pti_total_system * nanoseconds_per_tick) / 1000000000.0;
  usage->virtual_memory = proc.pti_virtual_size;
  usage->resident_memory = proc.pti_resident_size;
  return true;
}