xref: /6.6.0/sigar/programs/sigar_port.c (revision 829a4c03)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2011 Couchbase, Inc.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17#include <stdlib.h>
18#include <stdio.h>
19#include <stdint.h>
20#include <string.h>
21#include <sigar.h>
22
23#ifdef _WIN32
24#include <io.h>
25#include <fcntl.h>
26#endif
27
28#define DEFAULT(value, def) ((value) == SIGAR_FIELD_NOTIMPL ? (def) : (value))
29#define MUST_SUCCEED(body)                      \
30    do {                                        \
31        int _ret = (body);                      \
32        if (_ret != SIGAR_OK) {                 \
33            exit(1);                            \
34        }                                       \
35    } while (0)
36
37#define NUM_INTERESTING_PROCS 40
38#define PROCS_REFRESH_INTERVAL 20
39#define PROC_NAME_LEN 60
40
41struct proc {
42    sigar_pid_t pid;
43    sigar_pid_t ppid;
44    sigar_uint64_t start_time;
45    char name[PROC_NAME_LEN];
46};
47
48struct proc_stats {
49    char name[PROC_NAME_LEN];
50    uint32_t cpu_utilization;
51
52    uint64_t pid;
53    uint64_t ppid;
54
55    uint64_t mem_size;
56    uint64_t mem_resident;
57    uint64_t mem_share;
58    uint64_t minor_faults;
59    uint64_t major_faults;
60    uint64_t page_faults;
61};
62
63struct system_stats {
64    uint32_t version;
65    uint32_t struct_size;
66
67    uint64_t cpu_total_ms;
68    uint64_t cpu_idle_ms;
69
70    uint64_t swap_total;
71    uint64_t swap_used;
72    uint64_t swap_page_in;
73    uint64_t swap_page_out;
74
75    uint64_t mem_total;
76    uint64_t mem_used;
77    uint64_t mem_actual_used;
78    uint64_t mem_actual_free;
79
80    struct proc_stats interesting_procs[NUM_INTERESTING_PROCS];
81};
82
83static int is_interesting_process(const char *name)
84{
85    return (strcmp(name, "moxi") != 0 &&
86            strcmp(name, "inet_gethost") != 0 &&
87            strcmp(name, "memsup") != 0 &&
88            strcmp(name, "cpu_sup") != 0 &&
89            strcmp(name, "sh") != 0 &&
90            strcmp(name, "epmd") != 0);
91}
92
93static int proc_ppid_compare(const void *va, const void *vb)
94{
95    const struct proc *a = va;
96    const struct proc *b = vb;
97
98    if (a->ppid < b->ppid) {
99        return -1;
100    } else if (a->ppid > b->ppid) {
101        return 1;
102    } else {
103        return 0;
104    }
105}
106
107static int find_interesting_procs(sigar_t *sigar, sigar_pid_t babysitter_pid,
108                                  struct proc interesting_procs[NUM_INTERESTING_PROCS])
109{
110    unsigned long i;
111    int interesting_count = 0;
112
113    sigar_proc_list_t proc_list;
114
115    struct proc *procs;
116    struct proc *procs_end;
117
118    struct proc *babysitter_proc = NULL;
119
120    MUST_SUCCEED(sigar_proc_list_get(sigar, &proc_list));
121
122    if (proc_list.number == 0) {
123        goto find_interesting_procs_return;
124    }
125
126    procs = malloc(sizeof(struct proc) * proc_list.number);
127    if (procs == NULL) {
128        exit(1);
129    }
130
131    procs_end = procs;
132
133    for (i = 0; i < proc_list.number; ++i) {
134        sigar_pid_t pid = proc_list.data[i];
135
136        sigar_proc_state_t proc_state;
137        sigar_proc_cpu_t proc_cpu;
138
139        if (sigar_proc_state_get(sigar, pid, &proc_state) != SIGAR_OK) {
140            continue;
141        }
142
143        if (sigar_proc_cpu_get(sigar, pid, &proc_cpu) != SIGAR_OK) {
144            continue;
145        }
146
147        procs_end->pid = pid;
148        procs_end->ppid = proc_state.ppid;
149        procs_end->start_time = proc_cpu.start_time;
150        strncpy(procs_end->name, proc_state.name, PROC_NAME_LEN);
151
152        if (pid == babysitter_pid) {
153            babysitter_proc = procs_end;
154        }
155
156        ++procs_end;
157    }
158
159    // something went utterly wrong, we couldn't find babysitter
160    if (!babysitter_proc) {
161        exit(1);
162    }
163
164    interesting_procs[interesting_count++] = *babysitter_proc;
165
166    qsort(procs, procs_end - procs, sizeof(struct proc), proc_ppid_compare);
167
168    for (i = 0; i < interesting_count; ++i) {
169        sigar_pid_t ppid = interesting_procs[i].pid;
170        struct proc key = {0};
171        struct proc *child;
172
173        key.ppid = ppid;
174
175        child = bsearch(&key, procs, procs_end - procs, sizeof(struct proc),
176                        proc_ppid_compare);
177        if (!child) {
178            continue;
179        }
180
181        // which element of multiple equal elements returned is unspecified;
182        // so we need to check neighboring elements in both directions
183        while (child > procs && (child - 1)->ppid == ppid) {
184            --child;
185        }
186
187        while (child->ppid == ppid && child < procs_end) {
188            if (is_interesting_process(child->name) &&
189                interesting_count < NUM_INTERESTING_PROCS) {
190
191                interesting_procs[interesting_count++] = *child;
192            }
193
194            ++child;
195        }
196    }
197
198    free(procs);
199
200find_interesting_procs_return:
201    MUST_SUCCEED(sigar_proc_list_destroy(sigar, &proc_list));
202
203    return interesting_count;
204}
205
206static int populate_interesting_procs(sigar_t *sigar,
207                                      struct proc *procs, int procs_count,
208                                      struct system_stats *reply)
209{
210    int i;
211    int stale = 0;
212
213    sigar_proc_mem_t proc_mem;
214    sigar_proc_cpu_t proc_cpu;
215
216    struct proc_stats *child;
217
218    for (i = 0; i < procs_count; ++i) {
219        if (sigar_proc_mem_get(sigar, procs[i].pid, &proc_mem) != SIGAR_OK ||
220            sigar_proc_cpu_get(sigar, procs[i].pid, &proc_cpu) != SIGAR_OK ||
221            procs[i].start_time != proc_cpu.start_time)
222        {
223            stale = 1;
224            continue;
225        }
226
227        child = &reply->interesting_procs[i];
228
229        child->pid = procs[i].pid;
230        child->ppid = procs[i].ppid;
231        strncpy(child->name, procs[i].name, PROC_NAME_LEN);
232        child->cpu_utilization = DEFAULT((uint32_t) (100 * proc_cpu.percent), 0);
233        child->mem_size = DEFAULT(proc_mem.size, 0);
234        child->mem_resident = DEFAULT(proc_mem.resident, 0);
235        child->mem_share = DEFAULT(proc_mem.share, 0);
236        child->minor_faults = DEFAULT(proc_mem.minor_faults, 0);
237        child->major_faults = DEFAULT(proc_mem.major_faults, 0);
238        child->page_faults = DEFAULT(proc_mem.page_faults, 0);
239    }
240
241    return stale;
242}
243
244int main(void)
245{
246    sigar_t *sigar;
247    sigar_mem_t mem;
248    sigar_swap_t swap;
249    sigar_cpu_t cpu;
250    struct system_stats reply;
251
252    sigar_pid_t pid;
253    sigar_proc_state_t state;
254
255    sigar_pid_t child_vm_pid;
256    sigar_pid_t babysitter_pid;
257
258    int procs_stale = 1;
259    int procs_count;
260    struct proc procs[NUM_INTERESTING_PROCS];
261
262    int ticks_to_refresh = PROCS_REFRESH_INTERVAL;
263
264    MUST_SUCCEED(sigar_open(&sigar));
265
266    pid = sigar_pid_get(sigar);
267    MUST_SUCCEED(sigar_proc_state_get(sigar, pid, &state));
268    child_vm_pid = state.ppid;
269
270    MUST_SUCCEED(sigar_proc_state_get(sigar, child_vm_pid, &state));
271    babysitter_pid = state.ppid;
272
273#ifdef _WIN32
274    _setmode(1, _O_BINARY);
275    _setmode(0, _O_BINARY);
276#endif
277
278    while (!feof(stdin)) {
279        int req;
280        int rv = fread(&req, sizeof(req), 1, stdin);
281        if (rv < 1) {
282            continue;
283        }
284        if (req != 0) {
285            break;
286        }
287        memset(&reply, 0, sizeof(reply));
288        reply.version = 3;
289        reply.struct_size = sizeof(reply);
290
291        sigar_mem_get(sigar, &mem);
292        sigar_swap_get(sigar, &swap);
293        sigar_cpu_get(sigar, &cpu);
294
295        reply.cpu_total_ms = cpu.total;
296        reply.cpu_idle_ms = cpu.idle + cpu.wait;
297
298        reply.swap_total = swap.total;
299        reply.swap_used = swap.used;
300        reply.swap_page_in = swap.page_in;
301        reply.swap_page_out = swap.page_out;
302
303        reply.mem_total = mem.total;
304        reply.mem_used = mem.used;
305        reply.mem_actual_used = mem.actual_used;
306        reply.mem_actual_free = mem.actual_free;
307
308        if (procs_stale || ticks_to_refresh-- == 0) {
309            ticks_to_refresh = PROCS_REFRESH_INTERVAL;
310            procs_count = find_interesting_procs(sigar, babysitter_pid, procs);
311        }
312
313        procs_stale = populate_interesting_procs(sigar, procs, procs_count, &reply);
314
315        fwrite(&reply, sizeof(reply), 1, stdout);
316        fflush(stdout);
317    }
318
319    sigar_close(sigar);
320    return 0;
321}
322