-
Notifications
You must be signed in to change notification settings - Fork 0
/
resuse.c
289 lines (264 loc) · 8.41 KB
/
resuse.c
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/* resuse.c - process resource use library
Copyright (C) 2016 Jeramie Vens
Copyright (C) 1993, 1996 Free Software Foundation, Inc.
Written by Jeramie Vens ported from GNU Time
Written by David MacKenzie, with help from
[email protected] (Arne Henrik Juul)
and [email protected] (Francois Pinard).
This file is part of libresuse.
libresuse 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 3 of the License, or
(at your option) any later version.
libresuse 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 libresuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include "resuse.h"
#ifndef TICKS_PER_SEC
#define TICKS_PER_SEC 100
#endif
#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
#define UL unsigned long
static unsigned long ptok (unsigned long pages);
/**
* Start resource usage collection information.
* @details This will save the start time and scope information
* for the resource usage collector. This function should
* be called at the top of the scope for which resource
* information is being collected.
* @param resp
* The `struct resuse` structure to save the resource
* information to.
* @param scope
* The scope to collect resources for.
*/
void resuse_start(struct resuse * resp, enum resuse_scope scope)
{
gettimeofday (&resp->start, (struct timezone *) 0);
resp->scope = scope;
}
/**
* Stop collecting and save the current resource usage information.
* @details This function will save the current time and calculate
* the elapsed time since resuse_start() was called. It
* will also query the kernel for information about the
* resources used by the process or thread depending on
* scope.
* @param resp
* The `struct resuse` structure that was given to
* resuse_start().
*/
void resuse_end(struct resuse * resp)
{
int status;
gettimeofday (&resp->elapsed, (struct timezone *) 0);
// get resource usage from the kernel
getrusage(resp->scope, &resp->ru);
// calculate elapsed time
resp->elapsed.tv_sec -= resp->start.tv_sec;
if (resp->elapsed.tv_usec < resp->start.tv_usec)
{
// Manually carry a one from the seconds field.
resp->elapsed.tv_usec += 1000000;
--resp->elapsed.tv_sec;
}
resp->elapsed.tv_usec -= resp->start.tv_usec;
}
/**
* Print the resource usage as a formated string to the file stream.
* @details This function mimics fprintf(). It uses the format string
* to format the collected resource information. The format
* string uses `%' flags similar to printf() functions. To
* see all supported flags look at the projectes README.md
* @param fp
* The file stream to print the output to
* @param fmt
* The format string to be used. `%' flags will be
* replaced with approriate information from @param resp.
* @param resp
* The resuse structure that was passed to resuse_start()
* and resuse_end().
*/
int resuse_fprint(FILE * fp, const char * fmt, const struct resuse * resp)
{
unsigned long r;
unsigned long v;
r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
while (*fmt)
{
switch(*fmt)
{
case '%':
switch (*++fmt)
{
case '%': // Literal '%'
putc('%', fp);
break;
case 'D': // Average unshared data size
fprintf(fp, "%lu",
MSEC_TO_TICKS(v) == 0 ? 0 :
ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
break;
case 'E': // Elapsed real (wall clock) time.
if (resp->elapsed.tv_sec >= 3600) // one hour -> h:m:s
fprintf (fp, "%ld:%02ld:%02ld",
resp->elapsed.tv_sec / 3600,
(resp->elapsed.tv_sec % 3600) / 60,
resp->elapsed.tv_sec %60);
else // m:s.ms
fprintf (fp, "%ld:%02ld.%02ld",
resp->elapsed.tv_sec / 60,
resp->elapsed.tv_sec % 60,
resp->elapsed.tv_usec / 10000);
break;
case 'F': // Major page faults
fprintf (fp, "%ld", resp->ru.ru_majflt);
break;
case 'I': // Inputs
fprintf (fp, "%ld", resp->ru.ru_inblock);
break;
case 'K': // Average mem usage == data+stack+text
fprintf (fp, "%lu",
ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) +
ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
break;
case 'M': // Maximum resident set size
fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss));
break;
case 'O': // Outputs
fprintf (fp, "%ld", resp->ru.ru_oublock);
break;
case 'P': // Percent of CPU this job got.
if (r > 0)
fprintf (fp, "%lu%%", (v * 100 / r));
else
fprintf (fp, "?%%");
break;
case 'R': // Minor page faults (reclaims)
fprintf (fp, "%ld", resp->ru.ru_minflt);
break;
case 'S': // System time
fprintf (fp, "%ld.%02d",
resp->ru.ru_stime.tv_sec,
resp->ru.ru_stime.TV_MSEC / 10);
break;
case 'U': // User time
fprintf (fp, "%ld.%02ld",
resp->ru.ru_utime.tv_sec,
resp->ru.ru_utime.TV_MSEC / 10);
break;
case 'W': // Times swapped out
fprintf (fp, "%ld", resp->ru.ru_nswap);
break;
case 'X': // Average shared text size
fprintf (fp, "%lu",
MSEC_TO_TICKS (v) == 0 ? 0 :
ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
break;
case 'Z': // Page size
fprintf (fp, "%d", getpagesize());
break;
case 'c': // Involuntary context switches
fprintf (fp, "%ld", resp->ru.ru_nivcsw);
break;
case 'e': // Elapsed real time in seconds
fprintf (fp, "%ld.%02ld",
resp->elapsed.tv_sec,
resp->elapsed.tv_usec / 10000);
break;
case 'k': // Signals delivered
fprintf (fp, "%ld", resp->ru.ru_nsignals);
break;
case 'p': // Average stack segment
fprintf (fp, "%lu",
MSEC_TO_TICKS (v) == 0 ? 0 :
ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
break;
case 'r': // Incoming socket messages received
fprintf (fp, "%ld", resp->ru.ru_msgrcv);
break;
case 's': // Outgoing socket messages sent
fprintf (fp, "%ld", resp->ru.ru_msgsnd);
break;
case 't': // Average resident set size
fprintf (fp, "%lu",
MSEC_TO_TICKS (v) == 0 ? 0 :
ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v));
break;
case 'w': // Voluntary context switches
fprintf (fp, "%ld", resp->ru.ru_nvcsw);
break;
case '\0':
putc ('?', fp);
return -1;
default:
putc ('?', fp);
putc (*fmt, fp);
}
++fmt;
break;
case '\\': // Format escape
switch(*++fmt)
{
case 't':
putc ('\t', fp);
break;
case 'n':
putc ('\n', fp);
break;
case '\\':
putc ('\\', fp);
break;
default:
putc ('?', fp);
putc ('\\', fp);
putc (*fmt, fp);
}
++fmt;
break;
default:
putc (*fmt++, fp);
}
//if (ferror (fp))
// error (stderr, errno, "write error");
}
return 0;
}
/**
* Converte number of pages to size in KB
* @param pages
* The number of pages
* @return The size in KB of the given number of pages.
*/
static unsigned long ptok (unsigned long pages)
{
static unsigned long ps = 0;
unsigned long tmp;
static long size = LONG_MAX;
if (ps == 0)
ps = (long) getpagesize();
if (pages > (LONG_MAX / ps))
{ // could overflow
tmp = pages / 1024; // smaller first
size = tmp * ps; // then larger
}
else
{ // could underflow
tmp = pages * ps; // larger first
size = tmp / 1024; // then smaller
}
return size;
}