aboutsummaryrefslogtreecommitdiffstats
path: root/src/process.c
blob: 398d9e942e82f81f2ceebddeab761454b0b0fb0b (plain) (blame)
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/**
 *
 * cpulimit - a cpu limiter for Linux
 *
 * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

//TODO: add documentation to public functions

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h>
#include <sys/utsname.h>

#include "process.h"

#ifdef __APPLE__
#include <sys/sysctl.h>
#include <errno.h>
#endif

#ifdef __linux__
int get_proc_info(struct process *p, pid_t pid) {
/*	static char statfile[20];
	static char buffer[64];
	int ret;
	sprintf(statfile, "/proc/%d/stat", pid);
	FILE *fd = fopen(statfile, "r");
	if (fd==NULL) return -1;
	fgets(buffer, sizeof(buffer), fd);
	fclose(fd);

	char state;

    int n = sscanf(buffer, "%d %s %c %d %d %d %d %d "
		"%lu %lu %lu %lu %lu %lu %lu "
		"%ld %ld %ld %ld %ld %ld "
		"%lu ",
		&p->pid,
		&p->command,
		&state,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		&utime,&stime,&cutime,&cstime,
		NULL,NULL,NULL,NULL,
		&starttime,
	);*/
	return 0;
}
#elif defined __APPLE__
int get_proc_info(struct process *p, pid_t pid) {
	int err;
	struct kinfo_proc *result = NULL;
	size_t length;
	int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};

	/* We start by calling sysctl with result == NULL and length == 0.
	   That will succeed, and set length to the appropriate length.
	   We then allocate a buffer of that size and call sysctl again
	   with that buffer.
	*/
	length = 0;
	err = sysctl(mib, 4, NULL, &length, NULL, 0);
	if (err == -1) {
		err = errno;
	}
	if (err == 0) {
		result = malloc(length);
		err = sysctl(mib, 4, result, &length, NULL, 0);
		if (err == -1)
			err = errno;
		if (err == ENOMEM) {
			free(result); /* clean up */
			result = NULL;
		}
	}

	p->pid = result->kp_proc.p_pid;
	p->ppid = result->kp_eproc.e_ppid;
	p->starttime = result->kp_proc.p_starttime.tv_sec;
	p->last_jiffies = result->kp_proc.p_cpticks;
	//p_pctcpu

	return 0;
}
#endif

// returns the start time of a process (used with pid to identify a process)
static int get_starttime(pid_t pid)
{
#ifdef __linux__
	char file[20];
	char buffer[1024];
	sprintf(file, "/proc/%d/stat", pid);
	FILE *fd = fopen(file, "r");
		if (fd==NULL) return -1;
	if (fgets(buffer, sizeof(buffer), fd) == NULL) return -1;
	fclose(fd);
	char *p = buffer;
	p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
	int sp = 20;
	while (sp--)
		p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
	//start time of the process
	int time = atoi(p+1);
	return time;
#elif defined __APPLE__
	struct process proc;
	get_proc_info(&proc, pid);
	return proc.starttime;
#endif
}

static int get_jiffies(struct process *proc) {
#ifdef __linux__
	FILE *f = fopen(proc->stat_file, "r");
	if (f==NULL) return -1;
	if (fgets(proc->buffer, sizeof(proc->buffer),f) == NULL) return -1;
	fclose(f);
	char *p = proc->buffer;
	p = memchr(p+1,')', sizeof(proc->buffer) - (p-proc->buffer));
	int sp = 12;
	while (sp--)
		p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
	//user mode jiffies
	int utime = atoi(p+1);
	p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
	//kernel mode jiffies
	int ktime = atoi(p+1);
	return utime+ktime;
#elif defined __APPLE__
	struct process proc2;
	get_proc_info(&proc2, proc->pid);
	return proc2.last_jiffies;
#endif
}

//return t1-t2 in microseconds (no overflow checks, so better watch out!)
static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2)
{
	return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec);
}

/*static int*/ int process_update(struct process *proc) {
	//TODO: get any process statistic here
	//check that starttime is not changed(?), update jiffies, parent, zombie status
	return 0;
}

int process_init(struct process *proc, int pid)
{
	//general members
	proc->pid = pid;
	proc->starttime = get_starttime(pid);
	proc->cpu_usage = 0;
	memset(&(proc->last_sample), 0, sizeof(struct timeval));
	proc->last_jiffies = -1;
	//system dependent members
#ifdef __linux__
//TODO: delete these members for the sake of portability?
	//test /proc file descriptor for reading
	sprintf(proc->stat_file, "/proc/%d/stat", pid);
	FILE *fd = fopen(proc->stat_file, "r");
	if (fd == NULL) return 1;
	fclose(fd);
#endif
	return 0;
}

//parameter in range 0-1
#define ALFA 0.08

int process_monitor(struct process *proc)
{
	int j = get_jiffies(proc);
	if (j<0) return -1; //error retrieving jiffies count (maybe the process is dead)
	struct timeval now;
	gettimeofday(&now, NULL);
	if (proc->last_jiffies==-1) {
		//store current time
		proc->last_sample = now;
		//store current jiffies
		proc->last_jiffies = j;
		//it's the first sample, cannot figure out the cpu usage
		proc->cpu_usage = -1;
		return 0;
	}
	//time from previous sample (in ns)
	long dt = timediff(&now, &(proc->last_sample));
	//how many jiffies in dt?
	double max_jiffies = dt * HZ / 1000000.0;
	double sample = (j - proc->last_jiffies) / max_jiffies;
	if (proc->cpu_usage == -1) {
		//initialization
		proc->cpu_usage = sample;
	}
	else {
		//usage adjustment
		proc->cpu_usage = (1-ALFA) * proc->cpu_usage + ALFA * sample;
	}

	//store current time
	proc->last_sample = now;
	//store current jiffies
	proc->last_jiffies = j;
	
	return 0;
}

int process_close(struct process *proc)
{
	if (kill(proc->pid,SIGCONT)!=0) {
		fprintf(stderr,"Process %d is already dead!\n", proc->pid);
	}
	proc->pid = 0;
	return 0;
}
Un proyecto texto-plano.xyz