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