File: procinfo.cpp

package info (click to toggle)
boinc 8.2.8%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 104,744 kB
  • sloc: cpp: 172,971; php: 116,336; pascal: 56,058; xml: 17,863; python: 8,753; javascript: 6,538; sh: 5,343; objc: 2,322; ansic: 2,200; makefile: 2,167; perl: 1,843; sql: 832; java: 429; lisp: 47; csh: 30
file content (258 lines) | stat: -rw-r--r-- 8,702 bytes parent folder | download
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2011 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

// platform-independent process-enumeration functions

#if defined(_WIN32)
#include "boinc_win.h"
#include "win_util.h"
#else
#include "config.h"
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
#endif

#include "util.h"

#include "procinfo.h"

using std::vector;

// Scan the process table adding in CPU time and mem usage.
//
void add_child_totals(PROCINFO& procinfo, PROC_MAP& pm, PROC_MAP::iterator i) {
    PROCINFO parent = i->second;
    for (unsigned int j=0; j<parent.children.size(); j++) {
        int child_pid = parent.children[j];
        PROC_MAP::iterator i2 = pm.find(child_pid);
        if (i2 == pm.end()) continue;
        PROCINFO& p = i2->second;
        if (p.scanned) {
            return;     // cycle in graph - shouldn't happen
        }
        procinfo.kernel_time += p.kernel_time;
        procinfo.user_time += p.user_time;
        p.scanned = true;

        // only count process with most swap and memory
        if (p.swap_size > procinfo.swap_size) {
            procinfo.swap_size = p.swap_size;
        }
        if (p.working_set_size > procinfo.working_set_size) {
            procinfo.working_set_size = p.working_set_size;
        }

        p.is_boinc_app = true;
        add_child_totals(procinfo, pm, i2); // recursion - woo hoo!
    }
}

// Fill in the given PROCINFO (initially zero except for id)
// with totals from that process and all its descendants.
// Set PROCINFO.is_boinc_app for all of them.
//
void procinfo_app(
    PROCINFO& procinfo, vector<int>* other_pids, PROC_MAP& pm, char* graphics_exec_file
) {
    PROC_MAP::iterator i;
    for (i=pm.begin(); i!=pm.end(); ++i) {
        PROCINFO& p = i->second;
        if (p.id == procinfo.id
            || (other_pids && in_vector(p.id, *other_pids))
        ) {
            procinfo.kernel_time += p.kernel_time;
            procinfo.user_time += p.user_time;
            procinfo.swap_size += p.swap_size;
            procinfo.working_set_size += p.working_set_size;
            p.is_boinc_app = true;
            p.scanned = true;

            // look for child processes
            //
            add_child_totals(procinfo, pm, i);
        }
        if (graphics_exec_file && !strcmp(p.command, graphics_exec_file)) {
            p.is_boinc_app = true;
        }
    }
}

void find_children(PROC_MAP& pm) {
    PROC_MAP::iterator i;
    for (i=pm.begin(); i!=pm.end(); ++i) {
        int parentid = i->second.parentid;
        PROC_MAP::iterator j = pm.find(parentid);
        if (j == pm.end()) continue;    // should never happen
#ifdef _WIN32
        // In Windows:
        // 1) PIDs are reused, possibly quickly
        // 2) if a process creates children and then exits,
        //    the parentID of the children are not cleared,
        //    so they may soon refer to an unrelated process.
        //    (this is horrible design, BTW)
        // This can cause problems:
        // - when we abort a BOINC app we kill the process and its descendants
        //   (based on parent ID).  These could be unrelated processes.
        // - If a BOINC app gets a process ID that is the parentID of
        //   orphan processes, its CPU time will be computed incorrectly.
        // To fix this, don't create a parent/child link
        // if the parent was created after the child.
        //
        if (j->second.create_time.QuadPart > i->second.create_time.QuadPart) {
            continue;
        }
#endif
        j->second.children.push_back(i->first);
    }
}

// get resource usage of non-BOINC apps running above background priority.
// NOTE: this is flawed because it doesn't account for short-lived processes.
// It's not used on Win, Mac, or Linux,
// which have ways of getting total CPU usage.
// See client/app.cpp
//
void procinfo_non_boinc(PROCINFO& procinfo, PROC_MAP& pm) {
    procinfo.clear();
    PROC_MAP::iterator i;
    for (i=pm.begin(); i!=pm.end(); ++i) {
        PROCINFO& p = i->second;
#ifdef _WIN32
        if (p.id == 0) continue;    // idle process
#endif
        if (p.is_boinc_app) continue;
        if (p.is_low_priority) continue;

        // count VirtualBox process as BOINC;
        // on some systems they use nontrivial CPU time
        // TODO: do this only if we're running a vbox app
        //
        if (strstr(p.command, "VBoxSVC")) continue;
        if (strstr(p.command, "VBoxXPCOMIPCD")) continue;

#if 0
        if (p.user_time > .1) {
            fprintf(stderr, "non-boinc: %s (%d) %f %f\n", p.command, p.id, p.user_time, p.kernel_time);
        }
#endif
        procinfo.kernel_time += p.kernel_time;
        procinfo.user_time += p.user_time;
        procinfo.swap_size += p.swap_size;
        procinfo.working_set_size += p.working_set_size;
    }
#if 0
    fprintf(stderr,
        "total non-boinc: %f %f\n", procinfo.user_time, procinfo.kernel_time
    );
#endif
}

// get CPU time of things we don't want to count as non-BOINC-related
// - BOINC apps
// - low-priority processes
// - (if Vbox apps are running) Vbox processes
// - Windows: WSL daemon ('vmmem')
// - Mac: the VM used by Podman
// - Linux:
//      we don't account Docker/podman CPU time here,
//      since we don't know what the processes are.
//      Instead we do it in the client (in get_memory_usage())
//      by adding the current_cpu_time of Docker ACTIVE_TASKS
//
// This is subtracted from total CPU time to get
// the 'non-BOINC CPU time' used in computing preferences
//
// In each of the above cases we're adding the CPU times of a set of processes.
// If one of these processes exits,
// our next measurement will be lower than the previous one.
// We'll return a too-low value,
// leading to a too-high value of non-BOINC-related CPU,
// and possibly an incorrect suspension.
//
// To deal with this, maintain the total for each case separately.
// If any of them decreases, return reset = true,
// meaning that the value shouldn't get compared with previous values.
//
void boinc_related_cpu_time(
    PROC_MAP& pm, bool vbox_app_running,
    double& brc, bool& reset
) {
    static double prev_boinc_app = 0;
    static double prev_low_prio = 0;
    static double prev_vbox = 0;
    static double prev_docker = 0;

    double boinc_app = 0;
    double low_prio = 0;
    double vbox = 0;
    double docker = 0;

    PROC_MAP::iterator i;
    for (i=pm.begin(); i!=pm.end(); ++i) {
        PROCINFO& p = i->second;
#ifdef _WIN32
        if (p.id == 0) continue;    // idle process
#endif
        if (p.is_boinc_app) {
            boinc_app += (p.user_time + p.kernel_time);
        } else if (p.is_low_priority) {
            low_prio += (p.user_time + p.kernel_time);
        } else if (vbox_app_running && strstr(p.command, "VBox")) {
            // if a VBox app is running,
            // count VBox processes as BOINC-related
            // e.g. VBoxHeadless.exe and VBoxSVC.exe on Win
            vbox += (p.user_time + p.kernel_time);
#ifdef _WIN32
        } else if (strstr(p.command, "vmmem")) {
            docker += (p.user_time + p.kernel_time);
#endif
#ifdef __APPLE__
        } else if (strstr(p.command, "com.apple.Virtualization.VirtualMachine")) {
            docker += (p.user_time + p.kernel_time);
#endif
        }
    }
    brc = boinc_app + low_prio + vbox + docker;
    reset = (boinc_app < prev_boinc_app)
        || (low_prio < prev_low_prio)
        || (vbox < prev_vbox)
        || (docker < prev_docker);
    if (reset) {
        //fprintf(stderr, "boinc_related_cpu_time: reset\n");
    }
    prev_boinc_app = boinc_app;
    prev_low_prio = low_prio;
    prev_vbox = vbox;
    prev_docker = docker;
}

double process_tree_cpu_time(int pid) {
    PROC_MAP pm;
    PROCINFO procinfo;
    int retval;

    retval = procinfo_setup(pm);
    if (retval) return 0;

    procinfo.clear();
    procinfo.id = pid;
    procinfo_app(procinfo, NULL, pm, NULL);
    return procinfo.user_time + procinfo.kernel_time;
}