xref: /6.0.3/sigar/programs/sigar_port.c (revision dfaeb59a)
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 12
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
54    uint64_t mem_size;
55    uint64_t mem_resident;
56    uint64_t mem_share;
57    uint64_t minor_faults;
58    uint64_t major_faults;
59    uint64_t page_faults;
60};
61
62struct system_stats {
63    uint32_t version;
64    uint32_t struct_size;
65
66    uint64_t cpu_total_ms;
67    uint64_t cpu_idle_ms;
68
69    uint64_t swap_total;
70    uint64_t swap_used;
71    uint64_t swap_page_in;
72    uint64_t swap_page_out;
73
74    uint64_t mem_total;
75    uint64_t mem_used;
76    uint64_t mem_actual_used;
77    uint64_t mem_actual_free;
78
79    struct proc_stats interesting_procs[NUM_INTERESTING_PROCS];
80};
81
82static int is_interesting_process(const char *name)
83{
84    return (strcmp(name, "moxi") != 0 &&
85            strcmp(name, "inet_gethost") != 0 &&
86            strcmp(name, "memsup") != 0 &&
87            strcmp(name, "cpu_sup") != 0 &&
88            strcmp(name, "sh") != 0 &&
89            strcmp(name, "epmd") != 0);
90}
91
92static int proc_ppid_compare(const void *va, const void *vb)
93{
94    const struct proc *a = va;
95    const struct proc *b = vb;
96
97    if (a->ppid < b->ppid) {
98        return -1;
99    } else if (a->ppid > b->ppid) {
100        return 1;
101    } else {
102        return 0;
103    }
104}
105
106static int find_interesting_procs(sigar_t *sigar, sigar_pid_t babysitter_pid,
107                                  struct proc interesting_procs[NUM_INTERESTING_PROCS])
108{
109    unsigned long i;
110    int interesting_count = 0;
111
112    sigar_proc_list_t proc_list;
113
114    struct proc *procs;
115    struct proc *procs_end;
116
117    struct proc *babysitter_proc = NULL;
118
119    MUST_SUCCEED(sigar_proc_list_get(sigar, &proc_list));
120
121    if (proc_list.number == 0) {
122        goto find_interesting_procs_return;
123    }
124
125    procs = malloc(sizeof(struct proc) * proc_list.number);
126    if (procs == NULL) {
127        exit(1);
128    }
129
130    procs_end = procs;
131
132    for (i = 0; i < proc_list.number; ++i) {
133        sigar_pid_t pid = proc_list.data[i];
134
135        sigar_proc_state_t proc_state;
136        sigar_proc_cpu_t proc_cpu;
137
138        if (sigar_proc_state_get(sigar, pid, &proc_state) != SIGAR_OK) {
139            continue;
140        }
141
142        if (sigar_proc_cpu_get(sigar, pid, &proc_cpu) != SIGAR_OK) {
143            continue;
144        }
145
146        procs_end->pid = pid;
147        procs_end->ppid = proc_state.ppid;
148        procs_end->start_time = proc_cpu.start_time;
149        strncpy(procs_end->name, proc_state.name, PROC_NAME_LEN);
150
151        if (pid == babysitter_pid) {
152            babysitter_proc = procs_end;
153        }
154
155        ++procs_end;
156    }
157
158    // something went utterly wrong, we couldn't find babysitter
159    if (!babysitter_proc) {
160        exit(1);
161    }
162
163    interesting_procs[interesting_count++] = *babysitter_proc;
164
165    qsort(procs, procs_end - procs, sizeof(struct proc), proc_ppid_compare);
166
167    for (i = 0; i < interesting_count; ++i) {
168        sigar_pid_t ppid = interesting_procs[i].pid;
169        struct proc key = {0};
170        struct proc *child;
171
172        key.ppid = ppid;
173
174        child = bsearch(&key, procs, procs_end - procs, sizeof(struct proc),
175                        proc_ppid_compare);
176        if (!child) {
177            continue;
178        }
179
180        // which element of multiple equal elements returned is unspecified;
181        // so we need to check neighboring elements in both directions
182        while (child > procs && (child - 1)->ppid == ppid) {
183            --child;
184        }
185
186        while (child->ppid == ppid && child < procs_end) {
187            if (is_interesting_process(child->name) &&
188                interesting_count < NUM_INTERESTING_PROCS) {
189
190                interesting_procs[interesting_count++] = *child;
191            }
192
193            ++child;
194        }
195    }
196
197    free(procs);
198
199find_interesting_procs_return:
200    MUST_SUCCEED(sigar_proc_list_destroy(sigar, &proc_list));
201
202    return interesting_count;
203}
204
205static int populate_interesting_procs(sigar_t *sigar,
206                                      struct proc *procs, int procs_count,
207                                      struct system_stats *reply)
208{
209    int i;
210    int stale = 0;
211
212    sigar_proc_mem_t proc_mem;
213    sigar_proc_cpu_t proc_cpu;
214
215    struct proc_stats *child;
216
217    for (i = 0; i < procs_count; ++i) {
218        if (sigar_proc_mem_get(sigar, procs[i].pid, &proc_mem) != SIGAR_OK ||
219            sigar_proc_cpu_get(sigar, procs[i].pid, &proc_cpu) != SIGAR_OK ||
220            procs[i].start_time != proc_cpu.start_time)
221        {
222            stale = 1;
223            continue;
224        }
225
226        child = &reply->interesting_procs[i];
227
228        child->pid = procs[i].pid;
229        strncpy(child->name, procs[i].name, PROC_NAME_LEN);
230        child->cpu_utilization = DEFAULT((uint32_t) (100 * proc_cpu.percent), 0);
231        child->mem_size = DEFAULT(proc_mem.size, 0);
232        child->mem_resident = DEFAULT(proc_mem.resident, 0);
233        child->mem_share = DEFAULT(proc_mem.share, 0);
234        child->minor_faults = DEFAULT(proc_mem.minor_faults, 0);
235        child->major_faults = DEFAULT(proc_mem.major_faults, 0);
236        child->page_faults = DEFAULT(proc_mem.page_faults, 0);
237    }
238
239    return stale;
240}
241
242int main(void)
243{
244    sigar_t *sigar;
245    sigar_mem_t mem;
246    sigar_swap_t swap;
247    sigar_cpu_t cpu;
248    struct system_stats reply;
249
250    sigar_pid_t pid;
251    sigar_proc_state_t state;
252
253    sigar_pid_t child_vm_pid;
254    sigar_pid_t babysitter_pid;
255
256    int procs_stale = 1;
257    int procs_count;
258    struct proc procs[NUM_INTERESTING_PROCS];
259
260    int ticks_to_refresh = PROCS_REFRESH_INTERVAL;
261
262    MUST_SUCCEED(sigar_open(&sigar));
263
264    pid = sigar_pid_get(sigar);
265    MUST_SUCCEED(sigar_proc_state_get(sigar, pid, &state));
266    child_vm_pid = state.ppid;
267
268    MUST_SUCCEED(sigar_proc_state_get(sigar, child_vm_pid, &state));
269    babysitter_pid = state.ppid;
270
271#ifdef _WIN32
272    _setmode(1, _O_BINARY);
273    _setmode(0, _O_BINARY);
274#endif
275
276    while (!feof(stdin)) {
277        int req;
278        int rv = fread(&req, sizeof(req), 1, stdin);
279        if (rv < 1) {
280            continue;
281        }
282        if (req != 0) {
283            break;
284        }
285        memset(&reply, 0, sizeof(reply));
286        reply.version = 2;
287        reply.struct_size = sizeof(reply);
288
289        sigar_mem_get(sigar, &mem);
290        sigar_swap_get(sigar, &swap);
291        sigar_cpu_get(sigar, &cpu);
292
293        reply.cpu_total_ms = cpu.total;
294        reply.cpu_idle_ms = cpu.idle + cpu.wait;
295
296        reply.swap_total = swap.total;
297        reply.swap_used = swap.used;
298        reply.swap_page_in = swap.page_in;
299        reply.swap_page_out = swap.page_out;
300
301        reply.mem_total = mem.total;
302        reply.mem_used = mem.used;
303        reply.mem_actual_used = mem.actual_used;
304        reply.mem_actual_free = mem.actual_free;
305
306        if (procs_stale || ticks_to_refresh-- == 0) {
307            ticks_to_refresh = PROCS_REFRESH_INTERVAL;
308            procs_count = find_interesting_procs(sigar, babysitter_pid, procs);
309        }
310
311        procs_stale = populate_interesting_procs(sigar, procs, procs_count, &reply);
312
313        fwrite(&reply, sizeof(reply), 1, stdout);
314        fflush(stdout);
315    }
316
317    return 0;
318}
319