summaryrefslogtreecommitdiff
path: root/src/collectl2pcp/proc.c
blob: 516087c06d1187acb9c6a1a0e40df2c7a3b21bc7 (plain)
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
/*
 * Copyright (c) 2013 Red Hat Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program 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 General Public License
 * for more details.
 *
 * Handler for per-process metrics
 *
 * proc:28896 stat 28896 (bash) S 3574 28896 28896 34844 28896 4202496 2286 23473 1 21 4 2 486 129 20 0 1 0 125421198 119214080 85 18446744073709551615 4194304 5080360 140737040162832 140737040157848 212270400064 0 0 3686404 1266761467 18446744071582594345 0 0 17 2 0 0 0 0 0 9310744 9344396 14004224
 *
 */

#include "metrics.h"

#define ticks_to_msec(ticks) (1000ULL * strtoull(ticks, NULL, 0) / kernel_all_hz)

/* /proc/PID/stat fields (starting at fields[2]) */
#define PROC_PID_STAT_PID                0
#define PROC_PID_STAT_CMD                1
#define PROC_PID_STAT_STATE              2
#define PROC_PID_STAT_PPID               3
#define PROC_PID_STAT_PGRP               4
#define PROC_PID_STAT_SESSION            5
#define PROC_PID_STAT_TTY                6
#define PROC_PID_STAT_TTY_PGRP           7
#define PROC_PID_STAT_FLAGS              8
#define PROC_PID_STAT_MINFLT             9
#define PROC_PID_STAT_CMIN_FLT           10
#define PROC_PID_STAT_MAJ_FLT            11
#define PROC_PID_STAT_CMAJ_FLT           12
#define PROC_PID_STAT_UTIME              13
#define PROC_PID_STAT_STIME              14
#define PROC_PID_STAT_CUTIME             15
#define PROC_PID_STAT_CSTIME             16
#define PROC_PID_STAT_PRIORITY           17
#define PROC_PID_STAT_NICE               18
#define PROC_PID_STAT_REMOVED            19
#define PROC_PID_STAT_IT_REAL_VALUE      20
#define PROC_PID_STAT_START_TIME         21
#define PROC_PID_STAT_VSIZE              22
#define PROC_PID_STAT_RSS                23
#define PROC_PID_STAT_RSS_RLIM           24
#define PROC_PID_STAT_START_CODE         25
#define PROC_PID_STAT_END_CODE           26
#define PROC_PID_STAT_START_STACK        27
#define PROC_PID_STAT_ESP                28
#define PROC_PID_STAT_EIP                29
#define PROC_PID_STAT_SIGNAL             30
#define PROC_PID_STAT_BLOCKED            31
#define PROC_PID_STAT_SIGIGNORE          32
#define PROC_PID_STAT_SIGCATCH           33
#define PROC_PID_STAT_WCHAN              34
#define PROC_PID_STAT_NSWAP              35
#define PROC_PID_STAT_CNSWAP             36
#define PROC_PID_STAT_EXIT_SIGNAL        37
#define PROC_PID_STAT_PROCESSOR      	 38
#define PROC_PID_STAT_TTYNAME        	 39
#define PROC_PID_STAT_WCHAN_SYMBOL   	 40
#define PROC_PID_STAT_PSARGS         	 41

static char *inst;
static fields_t *proc_stat;

static int
find_command_start(const char *buf, size_t len)
{
    int i;

    /* skip over (minimal) leading "proc:N cmd " */
    for (i = 7; i < len - 4; i++)
	if (strncmp(&buf[i], "cmd", 4) == 0)
	    return i + 4;
    return -1;	/* wha?  cannot find the "cmd" component */
}

static void
inst_command_clean(char *command, size_t size)
{
    int i;

    /* command contains nulls - replace 'em */
    for (i = 0; i < size; i++) {
	if (!isprint(command[i]))
	    command[i] = ' ';
    }
    /* and trailing whitespace - clean that */
    while (--size) {
	if (isspace(command[size]))
	    command[size] = '\0';
	else
	    break;
    }
}

void
base_command_name(const char *command, char *base, size_t size)
{
    char *p, *start, *end;
    int kernel = (command[0] == '(');	/* kernel daemons heuristic */

    /* moral equivalent of basename, dealing with args stripping too */
    for (p = end = start = (char *)command; *p; end = ++p) {
	if (kernel)
	    continue;
	else if (*p == '/')
	    start = end = p+1;
	else if (isspace(*p))
	    break;
    }
    size--;	/* allow for a null */
    if (size > (end - start))
	size = (end - start);
    memcpy(base, start, size);
    base[size] = '\0';
}

int
proc_handler(handler_t *h, fields_t *f)
{
    int pid, off, bytes;
    char *command;
    size_t size;

    if (f->nfields < 2 || f->fieldlen[0] < 6)
    	return 0;

    if (strcmp(f->fields[1], "cmd") == 0) {
	/*
	 * e.g. :
	 * proc:27041 cmd /bin/sh /usr/prod/mts/common/bin/dblogin_gateway_reader
	 */
	if ((off = find_command_start(f->buf, f->len)) < 0)
	    return 0;
	size = f->len - off + 16;	/* +16 for the "%06d " pid */
	if ((inst = (char *)malloc(size)) == NULL)
	    return 0;
	sscanf(f->buf, "proc:%d", &pid);
	bytes = snprintf(inst, size, "%06d ", pid);

	/* f->buf contains nulls - so memcpy it then replace 'em */
	size = f->len - off - 1;
	command = inst + bytes;
	memcpy(command, f->buf + off, size);
	command[size] = '\0';
	inst_command_clean(command, size);
    }

    if (inst == NULL && strcmp(f->fields[1], "stat") == 0) {
	/* no instance yet, so stash it for later */
	proc_stat = fields_dup(f);
	return 0;
    }

    if (inst) {
	pmInDom indom = pmInDom_build(PROC_DOMAIN, PROC_PROC_INDOM);

	if ((command = strchr(inst, ' ')) != NULL) {
	    char cmdname[MAXPATHLEN];

	    command++;
	    base_command_name(command, &cmdname[0], sizeof(cmdname));
	    put_str_value("proc.psinfo.cmd", indom, inst, cmdname);
	    put_str_value("proc.psinfo.psargs", indom, inst, command);
	}

	/* emit the stashed proc_stat fields */
	put_ull_value("proc.psinfo.utime", indom, inst, ticks_to_msec(proc_stat->fields[PROC_PID_STAT_UTIME+2]));
	put_ull_value("proc.psinfo.stime", indom, inst, ticks_to_msec(proc_stat->fields[PROC_PID_STAT_STIME+2]));
	put_str_value("proc.psinfo.processor", indom, inst, proc_stat->fields[PROC_PID_STAT_PROCESSOR+2]);
	put_str_value("proc.psinfo.rss", indom, inst, proc_stat->fields[PROC_PID_STAT_RSS+2]);
	put_str_value("proc.psinfo.vsize", indom, inst, proc_stat->fields[PROC_PID_STAT_VSIZE+2]);
	put_str_value("proc.psinfo.sname", indom, inst, proc_stat->fields[PROC_PID_STAT_STATE+2]);
	/* and the rest .. */
	fields_free(proc_stat);
	proc_stat = NULL;

	/* TODO emit other stashed stuff .. */

	free(inst);
	inst = NULL;
    }

    return 0;
}