cde69a0b0a0eab09521aeb30f1d74e3513093864
[lttngtop.git] / src / common.c
1 /*
2 * Copyright (C) 2011-2012 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 bt_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 bt_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 bt_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 bt_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 uint64_t get_context_vtid(const struct bt_ctf_event *event)
88 {
89 const struct definition *scope;
90 uint64_t vtid;
91
92 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
93 vtid = bt_ctf_get_int64(bt_ctf_get_field(event,
94 scope, "_vtid"));
95 if (bt_ctf_field_get_error()) {
96 return -1ULL;
97 }
98
99 return vtid;
100 }
101
102 uint64_t get_context_vpid(const struct bt_ctf_event *event)
103 {
104 const struct definition *scope;
105 uint64_t vpid;
106
107 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
108 vpid = bt_ctf_get_int64(bt_ctf_get_field(event,
109 scope, "_vpid"));
110 if (bt_ctf_field_get_error()) {
111 return -1ULL;
112 }
113
114 return vpid;
115 }
116
117 uint64_t get_context_vppid(const struct bt_ctf_event *event)
118 {
119 const struct definition *scope;
120 uint64_t vppid;
121
122 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
123 vppid = bt_ctf_get_int64(bt_ctf_get_field(event,
124 scope, "_vppid"));
125 if (bt_ctf_field_get_error()) {
126 return -1ULL;
127 }
128
129 return vppid;
130 }
131
132 char *get_context_comm(const struct bt_ctf_event *event)
133 {
134 const struct bt_definition *scope;
135 char *comm;
136
137 scope = bt_ctf_get_top_level_scope(event, BT_STREAM_EVENT_CONTEXT);
138 comm = bt_ctf_get_char_array(bt_ctf_get_field(event,
139 scope, "_procname"));
140 if (bt_ctf_field_get_error()) {
141 fprintf(stderr, "Missing comm context info\n");
142 return NULL;
143 }
144
145 return comm;
146 }
147
148 /*
149 * To get the parent process, put the pid in the tid field
150 * because the parent process gets pid = tid
151 */
152 struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
153 {
154 struct processtop *tmp;
155
156 tmp = g_hash_table_lookup(ctx->process_hash_table,
157 (gconstpointer) (unsigned long) tid);
158
159 return tmp;
160 }
161
162 struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
163 unsigned long timestamp)
164 {
165 struct processtop *newproc;
166
167 if (opt_pid && tid != opt_pid)
168 return NULL;
169
170 /* if the PID already exists, we just rename the process */
171 /* FIXME : need to integrate with clone/fork/exit to be accurate */
172 newproc = find_process_tid(ctx, tid, comm);
173
174 if (!newproc) {
175 newproc = g_new0(struct processtop, 1);
176 newproc->tid = tid;
177 newproc->birth = timestamp;
178 newproc->process_files_table = g_ptr_array_new();
179 newproc->files_history = NULL;
180 newproc->totalfileread = 0;
181 newproc->totalfilewrite = 0;
182 newproc->fileread = 0;
183 newproc->filewrite = 0;
184 newproc->syscall_info = NULL;
185 newproc->threadparent = NULL;
186 newproc->threads = g_ptr_array_new();
187 newproc->perf = g_hash_table_new(g_str_hash, g_str_equal);
188 g_ptr_array_add(ctx->process_table, newproc);
189 g_hash_table_insert(ctx->process_hash_table,
190 (gpointer) (unsigned long) tid, newproc);
191
192 ctx->nbnewthreads++;
193 ctx->nbthreads++;
194 }
195 newproc->comm = strdup(comm);
196
197 return newproc;
198 }
199
200 struct processtop* update_proc(struct processtop* proc, int pid, int tid,
201 int ppid, int vpid, int vtid, int vppid, char *comm)
202 {
203 if (proc) {
204 proc->pid = pid;
205 proc->tid = tid;
206 proc->ppid = ppid;
207 proc->vpid = vpid;
208 proc->vtid = vtid;
209 proc->vppid = vppid;
210 if (strcmp(proc->comm, comm) != 0) {
211 free(proc->comm);
212 proc->comm = strdup(comm);
213 }
214 }
215 return proc;
216 }
217
218 /*
219 * This function just sets the time of death of a process.
220 * When we rotate the cputime we remove it from the process list.
221 */
222 void death_proc(struct lttngtop *ctx, int tid, char *comm,
223 unsigned long timestamp)
224 {
225 struct processtop *tmp;
226 tmp = find_process_tid(ctx, tid, comm);
227
228 g_hash_table_remove(ctx->process_hash_table,
229 (gpointer) (unsigned long) tid);
230 if (tmp && strcmp(tmp->comm, comm) == 0) {
231 tmp->death = timestamp;
232 ctx->nbdeadthreads++;
233 ctx->nbthreads--;
234 }
235 }
236
237 struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
238 unsigned long timestamp)
239 {
240 struct processtop *tmp;
241 tmp = find_process_tid(ctx, tid, comm);
242 if (tmp && strcmp(tmp->comm, comm) == 0)
243 return tmp;
244 return add_proc(ctx, tid, comm, timestamp);
245 }
246
247 struct processtop *get_proc_pid(struct lttngtop *ctx, int tid, int pid,
248 unsigned long timestamp)
249 {
250 struct processtop *tmp;
251 tmp = find_process_tid(ctx, tid, NULL);
252 if (tmp && tmp->pid == pid)
253 return tmp;
254 return add_proc(ctx, tid, "Unknown", timestamp);
255 }
256
257 void add_thread(struct processtop *parent, struct processtop *thread)
258 {
259 gint i;
260 struct processtop *tmp;
261
262 if (!parent)
263 return;
264
265 for (i = 0; i < parent->threads->len; i++) {
266 tmp = g_ptr_array_index(parent->threads, i);
267 if (tmp == thread)
268 return;
269 }
270 g_ptr_array_add(parent->threads, thread);
271 }
272
273 struct cputime* add_cpu(int cpu)
274 {
275 struct cputime *newcpu;
276
277 newcpu = g_new0(struct cputime, 1);
278 newcpu->id = cpu;
279 newcpu->current_task = NULL;
280 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
281
282 g_ptr_array_add(lttngtop.cpu_table, newcpu);
283
284 return newcpu;
285 }
286 struct cputime* get_cpu(int cpu)
287 {
288 gint i;
289 struct cputime *tmp;
290
291 for (i = 0; i < lttngtop.cpu_table->len; i++) {
292 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
293 if (tmp->id == cpu)
294 return tmp;
295 }
296
297 return add_cpu(cpu);
298 }
299
300 /*
301 * At the end of a sampling period, we need to display the cpu time for each
302 * process and to reset it to zero for the next period
303 */
304 void rotate_cputime(unsigned long end)
305 {
306 gint i;
307 struct cputime *tmp;
308 unsigned long elapsed;
309
310 for (i = 0; i < lttngtop.cpu_table->len; i++) {
311 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
312 elapsed = end - tmp->task_start;
313 if (tmp->current_task) {
314 tmp->current_task->totalcpunsec += elapsed;
315 tmp->current_task->threadstotalcpunsec += elapsed;
316 if (tmp->current_task->pid != tmp->current_task->tid &&
317 tmp->current_task->threadparent) {
318 tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
319 }
320 }
321 tmp->task_start = end;
322 }
323 }
324
325 void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
326 {
327 ((struct perfcounter*) value)->count = 0;
328 }
329
330 void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
331 {
332 struct perfcounter *newperf;
333
334 newperf = g_new0(struct perfcounter, 1);
335 newperf->count = ((struct perfcounter *) value)->count;
336 newperf->visible = ((struct perfcounter *) value)->visible;
337 newperf->sort = ((struct perfcounter *) value)->sort;
338 g_hash_table_insert((GHashTable *) new_table, strdup(key), newperf);
339 }
340
341 void copy_process_table(gpointer key, gpointer value, gpointer new_table)
342 {
343 g_hash_table_insert((GHashTable *) new_table, key, value);
344 }
345
346 void rotate_perfcounter() {
347 int i;
348 struct processtop *tmp;
349 for (i = 0; i < lttngtop.process_table->len; i++) {
350 tmp = g_ptr_array_index(lttngtop.process_table, i);
351 g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
352 }
353 }
354
355 void cleanup_processtop()
356 {
357 gint i, j;
358 struct processtop *tmp;
359 struct files *tmpf; /* a temporary file */
360
361 for (i = 0; i < lttngtop.process_table->len; i++) {
362 tmp = g_ptr_array_index(lttngtop.process_table, i);
363 tmp->totalcpunsec = 0;
364 tmp->threadstotalcpunsec = 0;
365 tmp->fileread = 0;
366 tmp->filewrite = 0;
367
368 for (j = 0; j < tmp->process_files_table->len; j++) {
369 tmpf = g_ptr_array_index(tmp->process_files_table, j);
370 if (tmpf != NULL) {
371 tmpf->read = 0;
372 tmpf->write = 0;
373
374 if (tmpf->flag == __NR_close)
375 g_ptr_array_index(
376 tmp->process_files_table, j
377 ) = NULL;
378 }
379 }
380 }
381 }
382
383 void reset_global_counters()
384 {
385 lttngtop.nbnewproc = 0;
386 lttngtop.nbdeadproc = 0;
387 lttngtop.nbnewthreads = 0;
388 lttngtop.nbdeadthreads = 0;
389 lttngtop.nbnewfiles = 0;
390 lttngtop.nbclosedfiles = 0;
391 }
392
393 void copy_global_counters(struct lttngtop *dst)
394 {
395 dst->nbproc = lttngtop.nbproc;
396 dst->nbnewproc = lttngtop.nbnewproc;
397 dst->nbdeadproc = lttngtop.nbdeadproc;
398 dst->nbthreads = lttngtop.nbthreads;
399 dst->nbnewthreads = lttngtop.nbnewthreads;
400 dst->nbdeadthreads = lttngtop.nbdeadthreads;
401 dst->nbfiles = lttngtop.nbfiles;
402 dst->nbnewfiles = lttngtop.nbnewfiles;
403 dst->nbclosedfiles = lttngtop.nbclosedfiles;
404 reset_global_counters();
405 }
406
407 struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
408 {
409 gint i, j;
410 unsigned long time;
411 struct lttngtop *dst;
412 struct processtop *tmp, *tmp2, *new;
413 struct cputime *tmpcpu, *newcpu;
414 struct files *tmpfile, *newfile;
415
416 dst = g_new0(struct lttngtop, 1);
417 dst->start = start;
418 dst->end = end;
419 copy_global_counters(dst);
420 dst->process_table = g_ptr_array_new();
421 dst->files_table = g_ptr_array_new();
422 dst->cpu_table = g_ptr_array_new();
423 dst->process_hash_table = g_hash_table_new(g_direct_hash, g_direct_equal);
424 g_hash_table_foreach(lttngtop.process_hash_table, copy_process_table,
425 dst->process_hash_table);
426
427 rotate_cputime(end);
428
429 for (i = 0; i < lttngtop.process_table->len; i++) {
430 tmp = g_ptr_array_index(lttngtop.process_table, i);
431 new = g_new0(struct processtop, 1);
432
433 memcpy(new, tmp, sizeof(struct processtop));
434 new->threads = g_ptr_array_new();
435 new->comm = strdup(tmp->comm);
436 new->process_files_table = g_ptr_array_new();
437 new->files_history = tmp->files_history;
438 new->perf = g_hash_table_new(g_str_hash, g_str_equal);
439 g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
440
441 /* compute the stream speed */
442 if (end - start != 0) {
443 time = (end - start) / NSEC_PER_SEC;
444 new->fileread = new->fileread/(time);
445 new->filewrite = new->filewrite/(time);
446 }
447
448 for (j = 0; j < tmp->process_files_table->len; j++) {
449 tmpfile = g_ptr_array_index(tmp->process_files_table, j);
450
451 newfile = malloc(sizeof(struct files));
452
453 if (tmpfile != NULL) {
454 memcpy(newfile, tmpfile, sizeof(struct files));
455 newfile->name = strdup(tmpfile->name);
456 newfile->ref = new;
457 g_ptr_array_add(new->process_files_table,
458 newfile);
459 g_ptr_array_add(dst->files_table, newfile);
460 } else {
461 g_ptr_array_add(new->process_files_table, NULL);
462 g_ptr_array_add(dst->files_table, NULL);
463 }
464 /*
465 * if the process died during the last period, we remove all
466 * files associated with if after the copy
467 */
468 if (tmp->death > 0 && tmp->death < end) {
469 /* FIXME : close the files before */
470 g_ptr_array_remove(tmp->process_files_table, tmpfile);
471 g_free(tmpfile);
472 }
473 }
474 g_ptr_array_add(dst->process_table, new);
475
476 /*
477 * if the process died during the last period, we remove it from
478 * the current process list after the copy
479 */
480 if (tmp->death > 0 && tmp->death < end) {
481 g_ptr_array_remove(lttngtop.process_table, tmp);
482 /* FIXME : TRUE does not mean clears the object in it */
483 g_ptr_array_free(tmp->threads, TRUE);
484 free(tmp->comm);
485 g_ptr_array_free(tmp->process_files_table, TRUE);
486 /* FIXME : clear elements */
487 g_hash_table_destroy(tmp->perf);
488 g_free(tmp);
489 }
490 }
491 rotate_perfcounter();
492
493 for (i = 0; i < lttngtop.cpu_table->len; i++) {
494 tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
495 newcpu = g_new0(struct cputime, 1);
496 memcpy(newcpu, tmpcpu, sizeof(struct cputime));
497 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
498 g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
499 /*
500 * note : we don't care about the current process pointer in the copy
501 * so the reference is invalid after the memcpy
502 */
503 g_ptr_array_add(dst->cpu_table, newcpu);
504 }
505 /* FIXME : better algo */
506 /* create the threads index if required */
507 for (i = 0; i < dst->process_table->len; i++) {
508 tmp = g_ptr_array_index(dst->process_table, i);
509 if (tmp->pid == tmp->tid) {
510 for (j = 0; j < dst->process_table->len; j++) {
511 tmp2 = g_ptr_array_index(dst->process_table, j);
512 if (tmp2->pid == tmp->pid) {
513 tmp2->threadparent = tmp;
514 g_ptr_array_add(tmp->threads, tmp2);
515 }
516 }
517 }
518 }
519
520 // update_global_stats(dst);
521 cleanup_processtop();
522
523 return dst;
524 }
525
526
527 enum bt_cb_ret handle_statedump_process_state(struct bt_ctf_event *call_data,
528 void *private_data)
529 {
530 const struct bt_definition *scope;
531 struct processtop *proc;
532 unsigned long timestamp;
533 int64_t pid, tid, ppid, vtid, vpid, vppid;
534 char *procname;
535
536 timestamp = bt_ctf_get_timestamp(call_data);
537 if (timestamp == -1ULL)
538 goto error;
539
540 scope = bt_ctf_get_top_level_scope(call_data,
541 BT_EVENT_FIELDS);
542 pid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
543 scope, "_pid"));
544 if (bt_ctf_field_get_error()) {
545 fprintf(stderr, "Missing pid context info\n");
546 goto error;
547 }
548 ppid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
549 scope, "_ppid"));
550 if (bt_ctf_field_get_error()) {
551 fprintf(stderr, "Missing ppid context info\n");
552 goto error;
553 }
554 tid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
555 scope, "_tid"));
556 if (bt_ctf_field_get_error()) {
557 fprintf(stderr, "Missing tid context info\n");
558 goto error;
559 }
560 vtid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
561 scope, "_vtid"));
562 if (bt_ctf_field_get_error()) {
563 fprintf(stderr, "Missing vtid context info\n");
564 goto error;
565 }
566 vpid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
567 scope, "_vpid"));
568 if (bt_ctf_field_get_error()) {
569 fprintf(stderr, "Missing vpid context info\n");
570 goto error;
571 }
572 vppid = bt_ctf_get_int64(bt_ctf_get_field(call_data,
573 scope, "_vppid"));
574 if (bt_ctf_field_get_error()) {
575 fprintf(stderr, "Missing vppid context info\n");
576 goto error;
577 }
578
579 scope = bt_ctf_get_top_level_scope(call_data,
580 BT_EVENT_FIELDS);
581 procname = bt_ctf_get_char_array(bt_ctf_get_field(call_data,
582 scope, "_name"));
583 if (bt_ctf_field_get_error()) {
584 fprintf(stderr, "Missing process name context info\n");
585 goto error;
586 }
587
588 proc = find_process_tid(&lttngtop, tid, procname);
589 if (proc == NULL)
590 proc = add_proc(&lttngtop, tid, procname, timestamp);
591 update_proc(proc, pid, tid, ppid, vpid, vtid, vppid, procname);
592
593 if (proc) {
594 free(proc->comm);
595 proc->comm = strdup(procname);
596 proc->pid = pid;
597 }
598
599 return BT_CB_OK;
600
601 error:
602 return BT_CB_ERROR_STOP;
603 }
604
605 struct tm format_timestamp(uint64_t timestamp)
606 {
607 struct tm tm;
608 uint64_t ts_sec = 0, ts_nsec;
609 time_t time_s;
610
611 ts_nsec = timestamp;
612 ts_sec += ts_nsec / NSEC_PER_SEC;
613 ts_nsec = ts_nsec % NSEC_PER_SEC;
614
615 time_s = (time_t) ts_sec;
616
617 localtime_r(&time_s, &tm);
618
619 return tm;
620 }
This page took 0.040039 seconds and 3 git commands to generate.