7c67264ebba1fdc652a1890c82aaee3266260a89
[lttngtop.git] / src / common.c
1 /*
2 * Copyright (C) 2011 Julien Desfossez
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #include <babeltrace/ctf/events.h>
19 #include <stdlib.h>
20 #include <linux/unistd.h>
21 #include <string.h>
22 #include "common.h"
23
24 uint64_t get_cpu_id(const struct bt_ctf_event *event)
25 {
26 const struct definition *scope;
27 uint64_t cpu_id;
28
29 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
30 cpu_id = bt_ctf_get_uint64(bt_ctf_get_field(event, scope, "cpu_id"));
31 if (bt_ctf_field_get_error()) {
32 fprintf(stderr, "[error] get cpu_id\n");
33 return -1ULL;
34 }
35
36 return cpu_id;
37 }
38
39 uint64_t get_context_tid(const struct bt_ctf_event *event)
40 {
41 const struct definition *scope;
42 uint64_t tid;
43
44 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
45 tid = bt_ctf_get_int64(bt_ctf_get_field(event,
46 scope, "_tid"));
47 if (bt_ctf_field_get_error()) {
48 fprintf(stderr, "Missing tid context info\n");
49 return -1ULL;
50 }
51
52 return tid;
53 }
54
55 uint64_t get_context_pid(const struct bt_ctf_event *event)
56 {
57 const struct definition *scope;
58 uint64_t pid;
59
60 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
61 pid = bt_ctf_get_int64(bt_ctf_get_field(event,
62 scope, "_pid"));
63 if (bt_ctf_field_get_error()) {
64 fprintf(stderr, "Missing pid context info\n");
65 return -1ULL;
66 }
67
68 return pid;
69 }
70
71 uint64_t get_context_ppid(const struct bt_ctf_event *event)
72 {
73 const struct definition *scope;
74 uint64_t ppid;
75
76 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
77 ppid = bt_ctf_get_int64(bt_ctf_get_field(event,
78 scope, "_ppid"));
79 if (bt_ctf_field_get_error()) {
80 fprintf(stderr, "Missing ppid context info\n");
81 return -1ULL;
82 }
83
84 return ppid;
85 }
86
87 char *get_context_comm(const struct bt_ctf_event *event)
88 {
89 const struct definition *scope;
90 char *comm;
91
92 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
93 comm = bt_ctf_get_char_array(bt_ctf_get_field(event,
94 scope, "_procname"));
95 if (bt_ctf_field_get_error()) {
96 fprintf(stderr, "Missing comm context info\n");
97 return NULL;
98 }
99
100 return comm;
101 }
102
103 struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
104 {
105 gint i;
106 struct processtop *tmp;
107
108 for (i = 0; i < ctx->process_table->len; i++) {
109 tmp = g_ptr_array_index(ctx->process_table, i);
110 if (tmp && tmp->tid == tid)
111 return tmp;
112 }
113 return NULL;
114 }
115
116 struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
117 unsigned long timestamp)
118 {
119 struct processtop *newproc;
120
121 /* if the PID already exists, we just rename the process */
122 /* FIXME : need to integrate with clone/fork/exit to be accurate */
123 newproc = find_process_tid(ctx, tid, comm);
124 if (!newproc) {
125 newproc = g_new0(struct processtop, 1);
126 newproc->tid = tid;
127 newproc->birth = timestamp;
128 newproc->process_files_table = g_ptr_array_new();
129 newproc->files_history = NULL;
130 newproc->totalfileread = 0;
131 newproc->totalfilewrite = 0;
132 newproc->fileread = 0;
133 newproc->filewrite = 0;
134 newproc->syscall_info = NULL;
135 newproc->threads = g_ptr_array_new();
136 newproc->perf = g_hash_table_new(g_str_hash, g_str_equal);
137 g_ptr_array_add(ctx->process_table, newproc);
138 }
139 newproc->comm = strdup(comm);
140
141 return newproc;
142 }
143
144 struct processtop* update_proc(struct processtop* proc, int pid, int tid,
145 int ppid, char *comm)
146 {
147 if (proc) {
148 proc->pid = pid;
149 proc->tid = tid;
150 proc->ppid = ppid;
151 if (strcmp(proc->comm, comm) != 0) {
152 free(proc->comm);
153 proc->comm = strdup(comm);
154 }
155 }
156 return proc;
157 }
158
159 /*
160 * This function just sets the time of death of a process.
161 * When we rotate the cputime we remove it from the process list.
162 */
163 void death_proc(struct lttngtop *ctx, int tid, char *comm,
164 unsigned long timestamp)
165 {
166 struct processtop *tmp;
167 tmp = find_process_tid(ctx, tid, comm);
168 if (tmp && strcmp(tmp->comm, comm) == 0)
169 tmp->death = timestamp;
170 }
171
172 struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
173 unsigned long timestamp)
174 {
175 struct processtop *tmp;
176 tmp = find_process_tid(ctx, tid, comm);
177 if (tmp && strcmp(tmp->comm, comm) == 0)
178 return tmp;
179 return add_proc(ctx, tid, comm, timestamp);
180 }
181
182 void add_thread(struct processtop *parent, struct processtop *thread)
183 {
184 gint i;
185 struct processtop *tmp;
186
187 for (i = 0; i < parent->threads->len; i++) {
188 tmp = g_ptr_array_index(parent->threads, i);
189 if (tmp == thread)
190 return;
191 }
192 g_ptr_array_add(parent->threads, thread);
193 }
194
195 struct cputime* add_cpu(int cpu)
196 {
197 struct cputime *newcpu;
198
199 newcpu = g_new0(struct cputime, 1);
200 newcpu->id = cpu;
201 newcpu->current_task = NULL;
202 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
203
204 g_ptr_array_add(lttngtop.cpu_table, newcpu);
205
206 return newcpu;
207 }
208 struct cputime* get_cpu(int cpu)
209 {
210 gint i;
211 struct cputime *tmp;
212
213 for (i = 0; i < lttngtop.cpu_table->len; i++) {
214 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
215 if (tmp->id == cpu)
216 return tmp;
217 }
218
219 return add_cpu(cpu);
220 }
221
222 /*
223 * At the end of a sampling period, we need to display the cpu time for each
224 * process and to reset it to zero for the next period
225 */
226 void rotate_cputime(unsigned long end)
227 {
228 gint i;
229 struct cputime *tmp;
230 unsigned long elapsed;
231
232 for (i = 0; i < lttngtop.cpu_table->len; i++) {
233 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
234 elapsed = end - tmp->task_start;
235 if (tmp->current_task) {
236 tmp->current_task->totalcpunsec += elapsed;
237 tmp->current_task->threadstotalcpunsec += elapsed;
238 if (tmp->current_task->pid != tmp->current_task->tid &&
239 tmp->current_task->threadparent) {
240 tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
241 }
242 }
243 tmp->task_start = end;
244 }
245 }
246
247 void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
248 {
249 ((struct perfcounter*) value)->count = 0;
250 }
251
252 void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
253 {
254 struct perfcounter *newperf;
255
256 newperf = g_new0(struct perfcounter, 1);
257 newperf->count = ((struct perfcounter *) value)->count;
258 newperf->visible = ((struct perfcounter *) value)->visible;
259 newperf->sort = ((struct perfcounter *) value)->sort;
260 g_hash_table_insert((GHashTable *) new_table, strdup(key), newperf);
261 }
262
263 void rotate_perfcounter() {
264 int i;
265 struct processtop *tmp;
266 for (i = 0; i < lttngtop.process_table->len; i++) {
267 tmp = g_ptr_array_index(lttngtop.process_table, i);
268 g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
269 }
270 }
271
272 void cleanup_processtop()
273 {
274 gint i, j;
275 struct processtop *tmp;
276 struct files *tmpf; /* a temporary file */
277
278 for (i = 0; i < lttngtop.process_table->len; i++) {
279 tmp = g_ptr_array_index(lttngtop.process_table, i);
280 tmp->totalcpunsec = 0;
281 tmp->threadstotalcpunsec = 0;
282 tmp->fileread = 0;
283 tmp->filewrite = 0;
284
285 for (j = 0; j < tmp->process_files_table->len; j++) {
286 tmpf = g_ptr_array_index(tmp->process_files_table, j);
287 if (tmpf != NULL) {
288 tmpf->read = 0;
289 tmpf->write = 0;
290
291 if (tmpf->flag == __NR_close)
292 g_ptr_array_index(
293 tmp->process_files_table, j
294 ) = NULL;
295 }
296 }
297 }
298 }
299
300 struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
301 {
302 gint i, j;
303 unsigned long time;
304 struct lttngtop *dst;
305 struct processtop *tmp, *tmp2, *new;
306 struct cputime *tmpcpu, *newcpu;
307 struct files *tmpfile, *newfile;
308
309 dst = g_new0(struct lttngtop, 1);
310 dst->start = start;
311 dst->end = end;
312 dst->process_table = g_ptr_array_new();
313 dst->files_table = g_ptr_array_new();
314 dst->cpu_table = g_ptr_array_new();
315 dst->perf_list = g_hash_table_new(g_str_hash, g_str_equal);
316
317 rotate_cputime(end);
318
319 g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
320 for (i = 0; i < lttngtop.process_table->len; i++) {
321 tmp = g_ptr_array_index(lttngtop.process_table, i);
322 new = g_new0(struct processtop, 1);
323
324 memcpy(new, tmp, sizeof(struct processtop));
325 new->threads = g_ptr_array_new();
326 new->comm = strdup(tmp->comm);
327 new->process_files_table = g_ptr_array_new();
328 new->files_history = tmp->files_history;
329 new->perf = g_hash_table_new(g_str_hash, g_str_equal);
330 g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
331
332 /* compute the stream speed */
333 if (end - start != 0) {
334 time = (end - start) / NSEC_PER_SEC;
335 new->fileread = new->fileread/(time);
336 new->filewrite = new->filewrite/(time);
337 }
338
339 for (j = 0; j < tmp->process_files_table->len; j++) {
340 tmpfile = g_ptr_array_index(tmp->process_files_table, j);
341
342 newfile = malloc(sizeof(struct files));
343
344 if (tmpfile != NULL) {
345 memcpy(newfile, tmpfile, sizeof(struct files));
346 newfile->name = strdup(tmpfile->name);
347 newfile->ref = new;
348 g_ptr_array_add(new->process_files_table,
349 newfile);
350 g_ptr_array_add(dst->files_table, newfile);
351 } else {
352 g_ptr_array_add(new->process_files_table, NULL);
353 g_ptr_array_add(dst->files_table, NULL);
354 }
355 /*
356 * if the process died during the last period, we remove all
357 * files associated with if after the copy
358 */
359 if (tmp->death > 0 && tmp->death < end) {
360 g_ptr_array_remove(tmp->process_files_table, tmpfile);
361 g_free(tmpfile);
362 }
363 }
364 g_ptr_array_add(dst->process_table, new);
365
366 /*
367 * if the process died during the last period, we remove it from
368 * the current process list after the copy
369 */
370 if (tmp->death > 0 && tmp->death < end) {
371 g_ptr_array_remove(lttngtop.process_table, tmp);
372 /* FIXME : TRUE does not mean clears the object in it */
373 g_ptr_array_free(tmp->threads, TRUE);
374 free(tmp->comm);
375 g_ptr_array_free(tmp->process_files_table, TRUE);
376 /* FIXME : clear elements */
377 g_hash_table_destroy(tmp->perf);
378 g_free(tmp);
379 }
380 }
381 rotate_perfcounter();
382
383 for (i = 0; i < lttngtop.cpu_table->len; i++) {
384 tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
385 newcpu = g_new0(struct cputime, 1);
386 memcpy(newcpu, tmpcpu, sizeof(struct cputime));
387 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
388 g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
389 /*
390 * note : we don't care about the current process pointer in the copy
391 * so the reference is invalid after the memcpy
392 */
393 g_ptr_array_add(dst->cpu_table, newcpu);
394 }
395 /* FIXME : better algo */
396 /* create the threads index if required */
397 for (i = 0; i < dst->process_table->len; i++) {
398 tmp = g_ptr_array_index(dst->process_table, i);
399 if (tmp->pid == tmp->tid) {
400 for (j = 0; j < dst->process_table->len; j++) {
401 tmp2 = g_ptr_array_index(dst->process_table, j);
402 if (tmp2->pid == tmp->pid) {
403 tmp2->threadparent = tmp;
404 g_ptr_array_add(tmp->threads, tmp2);
405 }
406 }
407 }
408 }
409
410 // update_global_stats(dst);
411 cleanup_processtop();
412
413 return dst;
414 }
415
This page took 0.054943 seconds and 3 git commands to generate.