1911cc028STrond Norbye/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2911cc028STrond Norbye/*
3911cc028STrond Norbye *     Copyright 2011 Couchbase, Inc.
4911cc028STrond Norbye *
5911cc028STrond Norbye *   Licensed under the Apache License, Version 2.0 (the "License");
6911cc028STrond Norbye *   you may not use this file except in compliance with the License.
7911cc028STrond Norbye *   You may obtain a copy of the License at
8911cc028STrond Norbye *
9911cc028STrond Norbye *       http://www.apache.org/licenses/LICENSE-2.0
10911cc028STrond Norbye *
11911cc028STrond Norbye *   Unless required by applicable law or agreed to in writing, software
12911cc028STrond Norbye *   distributed under the License is distributed on an "AS IS" BASIS,
13911cc028STrond Norbye *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14911cc028STrond Norbye *   See the License for the specific language governing permissions and
15911cc028STrond Norbye *   limitations under the License.
16911cc028STrond Norbye */
17ac483364SAliaksej Artamonau#include <stdlib.h>
18911cc028STrond Norbye#include <stdio.h>
19911cc028STrond Norbye#include <stdint.h>
20911cc028STrond Norbye#include <string.h>
21911cc028STrond Norbye#include <sigar.h>
22911cc028STrond Norbye
23911cc028STrond Norbye#ifdef _WIN32
24911cc028STrond Norbye#include <io.h>
25911cc028STrond Norbye#include <fcntl.h>
26911cc028STrond Norbye#endif
27911cc028STrond Norbye
28911cc028STrond Norbye#define DEFAULT(value, def) ((value) == SIGAR_FIELD_NOTIMPL ? (def) : (value))
29ac483364SAliaksej Artamonau#define MUST_SUCCEED(body)                      \
30ac483364SAliaksej Artamonau    do {                                        \
31ac483364SAliaksej Artamonau        int _ret = (body);                      \
32ac483364SAliaksej Artamonau        if (_ret != SIGAR_OK) {                 \
33ac483364SAliaksej Artamonau            exit(1);                            \
34ac483364SAliaksej Artamonau        }                                       \
35ac483364SAliaksej Artamonau    } while (0)
36911cc028STrond Norbye
37abc7020bSAliaksej Artamonau#define NUM_INTERESTING_PROCS 40
382694cfd2SAliaksej Artamonau#define PROCS_REFRESH_INTERVAL 20
39332532a2SAliaksej Artamonau#define PROC_NAME_LEN 60
40911cc028STrond Norbye
41911cc028STrond Norbyestruct proc {
42911cc028STrond Norbye    sigar_pid_t pid;
43907b36baSAliaksej Artamonau    sigar_pid_t ppid;
44911cc028STrond Norbye    sigar_uint64_t start_time;
45911cc028STrond Norbye    char name[PROC_NAME_LEN];
46911cc028STrond Norbye};
47911cc028STrond Norbye
48911cc028STrond Norbyestruct proc_stats {
49911cc028STrond Norbye    char name[PROC_NAME_LEN];
50911cc028STrond Norbye    uint32_t cpu_utilization;
51911cc028STrond Norbye
52911cc028STrond Norbye    uint64_t pid;
5376dad86dSAliaksej Artamonau    uint64_t ppid;
54911cc028STrond Norbye
55911cc028STrond Norbye    uint64_t mem_size;
56911cc028STrond Norbye    uint64_t mem_resident;
57911cc028STrond Norbye    uint64_t mem_share;
58911cc028STrond Norbye    uint64_t minor_faults;
59911cc028STrond Norbye    uint64_t major_faults;
60911cc028STrond Norbye    uint64_t page_faults;
61911cc028STrond Norbye};
62911cc028STrond Norbye
63911cc028STrond Norbyestruct system_stats {
64911cc028STrond Norbye    uint32_t version;
65911cc028STrond Norbye    uint32_t struct_size;
66911cc028STrond Norbye
67911cc028STrond Norbye    uint64_t cpu_total_ms;
68911cc028STrond Norbye    uint64_t cpu_idle_ms;
69911cc028STrond Norbye
70911cc028STrond Norbye    uint64_t swap_total;
71911cc028STrond Norbye    uint64_t swap_used;
72911cc028STrond Norbye    uint64_t swap_page_in;
73911cc028STrond Norbye    uint64_t swap_page_out;
74911cc028STrond Norbye
75911cc028STrond Norbye    uint64_t mem_total;
76911cc028STrond Norbye    uint64_t mem_used;
77911cc028STrond Norbye    uint64_t mem_actual_used;
78911cc028STrond Norbye    uint64_t mem_actual_free;
79911cc028STrond Norbye
80911cc028STrond Norbye    struct proc_stats interesting_procs[NUM_INTERESTING_PROCS];
81911cc028STrond Norbye};
82911cc028STrond Norbye
83907b36baSAliaksej Artamonaustatic int is_interesting_process(const char *name)
84907b36baSAliaksej Artamonau{
85dfaeb59aSAliaksej Artamonau    return (strcmp(name, "moxi") != 0 &&
86dfaeb59aSAliaksej Artamonau            strcmp(name, "inet_gethost") != 0 &&
87dfaeb59aSAliaksej Artamonau            strcmp(name, "memsup") != 0 &&
88dfaeb59aSAliaksej Artamonau            strcmp(name, "cpu_sup") != 0 &&
89dfaeb59aSAliaksej Artamonau            strcmp(name, "sh") != 0 &&
90dfaeb59aSAliaksej Artamonau            strcmp(name, "epmd") != 0);
91907b36baSAliaksej Artamonau}
92907b36baSAliaksej Artamonau
93907b36baSAliaksej Artamonaustatic int proc_ppid_compare(const void *va, const void *vb)
94907b36baSAliaksej Artamonau{
95907b36baSAliaksej Artamonau    const struct proc *a = va;
96907b36baSAliaksej Artamonau    const struct proc *b = vb;
97907b36baSAliaksej Artamonau
98907b36baSAliaksej Artamonau    if (a->ppid < b->ppid) {
99907b36baSAliaksej Artamonau        return -1;
100907b36baSAliaksej Artamonau    } else if (a->ppid > b->ppid) {
101907b36baSAliaksej Artamonau        return 1;
102907b36baSAliaksej Artamonau    } else {
103907b36baSAliaksej Artamonau        return 0;
104907b36baSAliaksej Artamonau    }
105907b36baSAliaksej Artamonau}
106907b36baSAliaksej Artamonau
107907b36baSAliaksej Artamonaustatic int find_interesting_procs(sigar_t *sigar, sigar_pid_t babysitter_pid,
108907b36baSAliaksej Artamonau                                  struct proc interesting_procs[NUM_INTERESTING_PROCS])
109911cc028STrond Norbye{
110911cc028STrond Norbye    unsigned long i;
111907b36baSAliaksej Artamonau    int interesting_count = 0;
112911cc028STrond Norbye
113911cc028STrond Norbye    sigar_proc_list_t proc_list;
114907b36baSAliaksej Artamonau
115907b36baSAliaksej Artamonau    struct proc *procs;
116907b36baSAliaksej Artamonau    struct proc *procs_end;
117907b36baSAliaksej Artamonau
118907b36baSAliaksej Artamonau    struct proc *babysitter_proc = NULL;
119911cc028STrond Norbye
120ac483364SAliaksej Artamonau    MUST_SUCCEED(sigar_proc_list_get(sigar, &proc_list));
121911cc028STrond Norbye
122907b36baSAliaksej Artamonau    if (proc_list.number == 0) {
123907b36baSAliaksej Artamonau        goto find_interesting_procs_return;
124907b36baSAliaksej Artamonau    }
125907b36baSAliaksej Artamonau
126907b36baSAliaksej Artamonau    procs = malloc(sizeof(struct proc) * proc_list.number);
127907b36baSAliaksej Artamonau    if (procs == NULL) {
128907b36baSAliaksej Artamonau        exit(1);
129907b36baSAliaksej Artamonau    }
130907b36baSAliaksej Artamonau
131907b36baSAliaksej Artamonau    procs_end = procs;
132907b36baSAliaksej Artamonau
133911cc028STrond Norbye    for (i = 0; i < proc_list.number; ++i) {
134907b36baSAliaksej Artamonau        sigar_pid_t pid = proc_list.data[i];
135907b36baSAliaksej Artamonau
136907b36baSAliaksej Artamonau        sigar_proc_state_t proc_state;
137907b36baSAliaksej Artamonau        sigar_proc_cpu_t proc_cpu;
138911cc028STrond Norbye
139911cc028STrond Norbye        if (sigar_proc_state_get(sigar, pid, &proc_state) != SIGAR_OK) {
140911cc028STrond Norbye            continue;
141911cc028STrond Norbye        }
142911cc028STrond Norbye
143907b36baSAliaksej Artamonau        if (sigar_proc_cpu_get(sigar, pid, &proc_cpu) != SIGAR_OK) {
144907b36baSAliaksej Artamonau            continue;
145907b36baSAliaksej Artamonau        }
146911cc028STrond Norbye
147907b36baSAliaksej Artamonau        procs_end->pid = pid;
148907b36baSAliaksej Artamonau        procs_end->ppid = proc_state.ppid;
149907b36baSAliaksej Artamonau        procs_end->start_time = proc_cpu.start_time;
150907b36baSAliaksej Artamonau        strncpy(procs_end->name, proc_state.name, PROC_NAME_LEN);
151911cc028STrond Norbye
152907b36baSAliaksej Artamonau        if (pid == babysitter_pid) {
153907b36baSAliaksej Artamonau            babysitter_proc = procs_end;
154907b36baSAliaksej Artamonau        }
155907b36baSAliaksej Artamonau
156907b36baSAliaksej Artamonau        ++procs_end;
157907b36baSAliaksej Artamonau    }
158907b36baSAliaksej Artamonau
159907b36baSAliaksej Artamonau    // something went utterly wrong, we couldn't find babysitter
160907b36baSAliaksej Artamonau    if (!babysitter_proc) {
161907b36baSAliaksej Artamonau        exit(1);
162907b36baSAliaksej Artamonau    }
163907b36baSAliaksej Artamonau
164907b36baSAliaksej Artamonau    interesting_procs[interesting_count++] = *babysitter_proc;
165907b36baSAliaksej Artamonau
166907b36baSAliaksej Artamonau    qsort(procs, procs_end - procs, sizeof(struct proc), proc_ppid_compare);
167907b36baSAliaksej Artamonau
168907b36baSAliaksej Artamonau    for (i = 0; i < interesting_count; ++i) {
169907b36baSAliaksej Artamonau        sigar_pid_t ppid = interesting_procs[i].pid;
170907b36baSAliaksej Artamonau        struct proc key = {0};
171907b36baSAliaksej Artamonau        struct proc *child;
172911cc028STrond Norbye
173907b36baSAliaksej Artamonau        key.ppid = ppid;
174911cc028STrond Norbye
175907b36baSAliaksej Artamonau        child = bsearch(&key, procs, procs_end - procs, sizeof(struct proc),
176907b36baSAliaksej Artamonau                        proc_ppid_compare);
177907b36baSAliaksej Artamonau        if (!child) {
178907b36baSAliaksej Artamonau            continue;
179907b36baSAliaksej Artamonau        }
180907b36baSAliaksej Artamonau
181907b36baSAliaksej Artamonau        // which element of multiple equal elements returned is unspecified;
182907b36baSAliaksej Artamonau        // so we need to check neighboring elements in both directions
183907b36baSAliaksej Artamonau        while (child > procs && (child - 1)->ppid == ppid) {
184907b36baSAliaksej Artamonau            --child;
185907b36baSAliaksej Artamonau        }
186907b36baSAliaksej Artamonau
187907b36baSAliaksej Artamonau        while (child->ppid == ppid && child < procs_end) {
188907b36baSAliaksej Artamonau            if (is_interesting_process(child->name) &&
189907b36baSAliaksej Artamonau                interesting_count < NUM_INTERESTING_PROCS) {
190907b36baSAliaksej Artamonau
191907b36baSAliaksej Artamonau                interesting_procs[interesting_count++] = *child;
192911cc028STrond Norbye            }
193907b36baSAliaksej Artamonau
194907b36baSAliaksej Artamonau            ++child;
195911cc028STrond Norbye        }
196911cc028STrond Norbye    }
197911cc028STrond Norbye
198907b36baSAliaksej Artamonau    free(procs);
199907b36baSAliaksej Artamonau
200907b36baSAliaksej Artamonaufind_interesting_procs_return:
201ac483364SAliaksej Artamonau    MUST_SUCCEED(sigar_proc_list_destroy(sigar, &proc_list));
202911cc028STrond Norbye
203907b36baSAliaksej Artamonau    return interesting_count;
204911cc028STrond Norbye}
205911cc028STrond Norbye
206911cc028STrond Norbyestatic int populate_interesting_procs(sigar_t *sigar,
207911cc028STrond Norbye                                      struct proc *procs, int procs_count,
208911cc028STrond Norbye                                      struct system_stats *reply)
209911cc028STrond Norbye{
210911cc028STrond Norbye    int i;
211911cc028STrond Norbye    int stale = 0;
212911cc028STrond Norbye
213911cc028STrond Norbye    sigar_proc_mem_t proc_mem;
214911cc028STrond Norbye    sigar_proc_cpu_t proc_cpu;
215911cc028STrond Norbye
216911cc028STrond Norbye    struct proc_stats *child;
217911cc028STrond Norbye
218911cc028STrond Norbye    for (i = 0; i < procs_count; ++i) {
219911cc028STrond Norbye        if (sigar_proc_mem_get(sigar, procs[i].pid, &proc_mem) != SIGAR_OK ||
220911cc028STrond Norbye            sigar_proc_cpu_get(sigar, procs[i].pid, &proc_cpu) != SIGAR_OK ||
221911cc028STrond Norbye            procs[i].start_time != proc_cpu.start_time)
222911cc028STrond Norbye        {
223911cc028STrond Norbye            stale = 1;
224911cc028STrond Norbye            continue;
225911cc028STrond Norbye        }
226911cc028STrond Norbye
227911cc028STrond Norbye        child = &reply->interesting_procs[i];
228911cc028STrond Norbye
229911cc028STrond Norbye        child->pid = procs[i].pid;
23076dad86dSAliaksej Artamonau        child->ppid = procs[i].ppid;
231911cc028STrond Norbye        strncpy(child->name, procs[i].name, PROC_NAME_LEN);
232911cc028STrond Norbye        child->cpu_utilization = DEFAULT((uint32_t) (100 * proc_cpu.percent), 0);
233911cc028STrond Norbye        child->mem_size = DEFAULT(proc_mem.size, 0);
234911cc028STrond Norbye        child->mem_resident = DEFAULT(proc_mem.resident, 0);
235911cc028STrond Norbye        child->mem_share = DEFAULT(proc_mem.share, 0);
236911cc028STrond Norbye        child->minor_faults = DEFAULT(proc_mem.minor_faults, 0);
237911cc028STrond Norbye        child->major_faults = DEFAULT(proc_mem.major_faults, 0);
238911cc028STrond Norbye        child->page_faults = DEFAULT(proc_mem.page_faults, 0);
239911cc028STrond Norbye    }
240911cc028STrond Norbye
241911cc028STrond Norbye    return stale;
242911cc028STrond Norbye}
243911cc028STrond Norbye
244911cc028STrond Norbyeint main(void)
245911cc028STrond Norbye{
246911cc028STrond Norbye    sigar_t *sigar;
247911cc028STrond Norbye    sigar_mem_t mem;
248911cc028STrond Norbye    sigar_swap_t swap;
249911cc028STrond Norbye    sigar_cpu_t cpu;
250911cc028STrond Norbye    struct system_stats reply;
251911cc028STrond Norbye
252911cc028STrond Norbye    sigar_pid_t pid;
253911cc028STrond Norbye    sigar_proc_state_t state;
254911cc028STrond Norbye
255911cc028STrond Norbye    sigar_pid_t child_vm_pid;
256911cc028STrond Norbye    sigar_pid_t babysitter_pid;
257911cc028STrond Norbye
258911cc028STrond Norbye    int procs_stale = 1;
259911cc028STrond Norbye    int procs_count;
260911cc028STrond Norbye    struct proc procs[NUM_INTERESTING_PROCS];
261911cc028STrond Norbye
2622694cfd2SAliaksej Artamonau    int ticks_to_refresh = PROCS_REFRESH_INTERVAL;
2632694cfd2SAliaksej Artamonau
264ac483364SAliaksej Artamonau    MUST_SUCCEED(sigar_open(&sigar));
265911cc028STrond Norbye
266911cc028STrond Norbye    pid = sigar_pid_get(sigar);
267ac483364SAliaksej Artamonau    MUST_SUCCEED(sigar_proc_state_get(sigar, pid, &state));
268911cc028STrond Norbye    child_vm_pid = state.ppid;
269911cc028STrond Norbye
270ac483364SAliaksej Artamonau    MUST_SUCCEED(sigar_proc_state_get(sigar, child_vm_pid, &state));
271911cc028STrond Norbye    babysitter_pid = state.ppid;
272911cc028STrond Norbye
273911cc028STrond Norbye#ifdef _WIN32
274911cc028STrond Norbye    _setmode(1, _O_BINARY);
275911cc028STrond Norbye    _setmode(0, _O_BINARY);
276911cc028STrond Norbye#endif
277911cc028STrond Norbye
278911cc028STrond Norbye    while (!feof(stdin)) {
279911cc028STrond Norbye        int req;
280911cc028STrond Norbye        int rv = fread(&req, sizeof(req), 1, stdin);
281911cc028STrond Norbye        if (rv < 1) {
282911cc028STrond Norbye            continue;
283911cc028STrond Norbye        }
284911cc028STrond Norbye        if (req != 0) {
285911cc028STrond Norbye            break;
286911cc028STrond Norbye        }
287911cc028STrond Norbye        memset(&reply, 0, sizeof(reply));
288332532a2SAliaksej Artamonau        reply.version = 3;
289911cc028STrond Norbye        reply.struct_size = sizeof(reply);
290911cc028STrond Norbye
291911cc028STrond Norbye        sigar_mem_get(sigar, &mem);
292911cc028STrond Norbye        sigar_swap_get(sigar, &swap);
293911cc028STrond Norbye        sigar_cpu_get(sigar, &cpu);
294911cc028STrond Norbye
295911cc028STrond Norbye        reply.cpu_total_ms = cpu.total;
296911cc028STrond Norbye        reply.cpu_idle_ms = cpu.idle + cpu.wait;
297911cc028STrond Norbye
298911cc028STrond Norbye        reply.swap_total = swap.total;
299911cc028STrond Norbye        reply.swap_used = swap.used;
300911cc028STrond Norbye        reply.swap_page_in = swap.page_in;
301911cc028STrond Norbye        reply.swap_page_out = swap.page_out;
302911cc028STrond Norbye
303911cc028STrond Norbye        reply.mem_total = mem.total;
304911cc028STrond Norbye        reply.mem_used = mem.used;
305911cc028STrond Norbye        reply.mem_actual_used = mem.actual_used;
306911cc028STrond Norbye        reply.mem_actual_free = mem.actual_free;
307911cc028STrond Norbye
3082694cfd2SAliaksej Artamonau        if (procs_stale || ticks_to_refresh-- == 0) {
3092694cfd2SAliaksej Artamonau            ticks_to_refresh = PROCS_REFRESH_INTERVAL;
310911cc028STrond Norbye            procs_count = find_interesting_procs(sigar, babysitter_pid, procs);
311911cc028STrond Norbye        }
312911cc028STrond Norbye
313911cc028STrond Norbye        procs_stale = populate_interesting_procs(sigar, procs, procs_count, &reply);
314911cc028STrond Norbye
315911cc028STrond Norbye        fwrite(&reply, sizeof(reply), 1, stdout);
316911cc028STrond Norbye        fflush(stdout);
317911cc028STrond Norbye    }
318911cc028STrond Norbye
319829a4c03STrond Norbye    sigar_close(sigar);
320911cc028STrond Norbye    return 0;
321911cc028STrond Norbye}