2 * Copyright (C) 2011-2012 Julien Desfossez
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;
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.
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.
18 #include <babeltrace/ctf/events.h>
20 #include <linux/unistd.h>
24 uint64_t get_cpu_id(const struct bt_ctf_event
*event
)
26 const struct bt_definition
*scope
;
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");
39 uint64_t get_context_tid(const struct bt_ctf_event
*event
)
41 const struct bt_definition
*scope
;
44 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
45 tid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
47 if (bt_ctf_field_get_error()) {
48 tid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
50 if (bt_ctf_field_get_error()) {
51 fprintf(stderr
, "Missing tid context info\n");
59 uint64_t get_context_pid(const struct bt_ctf_event
*event
)
61 const struct bt_definition
*scope
;
64 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
65 pid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
67 if (bt_ctf_field_get_error()) {
69 pid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
71 if (bt_ctf_field_get_error()) {
72 fprintf(stderr
, "Missing pid context info\n");
80 uint64_t get_context_ppid(const struct bt_ctf_event
*event
)
82 const struct bt_definition
*scope
;
85 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
86 ppid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
88 if (bt_ctf_field_get_error()) {
95 uint64_t get_context_vtid(const struct bt_ctf_event
*event
)
97 const struct bt_definition
*scope
;
100 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
101 vtid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
103 if (bt_ctf_field_get_error()) {
110 uint64_t get_context_vpid(const struct bt_ctf_event
*event
)
112 const struct bt_definition
*scope
;
115 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
116 vpid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
118 if (bt_ctf_field_get_error()) {
125 uint64_t get_context_vppid(const struct bt_ctf_event
*event
)
127 const struct bt_definition
*scope
;
130 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
131 vppid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
133 if (bt_ctf_field_get_error()) {
140 char *get_context_comm(const struct bt_ctf_event
*event
)
142 const struct bt_definition
*scope
;
145 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
146 comm
= bt_ctf_get_char_array(bt_ctf_get_field(event
,
147 scope
, "_procname"));
148 if (bt_ctf_field_get_error()) {
149 fprintf(stderr
, "Missing comm context info\n");
156 char *get_context_hostname(const struct bt_ctf_event
*event
)
158 const struct bt_definition
*scope
;
161 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
162 hostname
= bt_ctf_get_char_array(bt_ctf_get_field(event
,
163 scope
, "_hostname"));
164 if (bt_ctf_field_get_error()) {
172 * To get the parent process, put the pid in the tid field
173 * because the parent process gets pid = tid
175 struct processtop
*find_process_tid(struct lttngtop
*ctx
, int tid
, const char *comm
)
177 struct processtop
*tmp
;
179 tmp
= g_hash_table_lookup(ctx
->process_hash_table
,
180 (gconstpointer
) (unsigned long) tid
);
185 struct processtop
* add_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
186 unsigned long timestamp
, char *hostname
)
188 struct processtop
*newproc
;
191 /* if the PID already exists, we just rename the process */
192 /* FIXME : need to integrate with clone/fork/exit to be accurate */
193 newproc
= find_process_tid(ctx
, tid
, comm
);
196 newproc
= g_new0(struct processtop
, 1);
198 newproc
->birth
= timestamp
;
199 newproc
->process_files_table
= g_ptr_array_new();
200 newproc
->files_history
= NULL
;
201 newproc
->totalfileread
= 0;
202 newproc
->totalfilewrite
= 0;
203 newproc
->fileread
= 0;
204 newproc
->filewrite
= 0;
205 newproc
->syscall_info
= NULL
;
206 newproc
->threadparent
= NULL
;
207 newproc
->threads
= g_ptr_array_new();
208 newproc
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
209 g_ptr_array_add(ctx
->process_table
, newproc
);
210 g_hash_table_insert(ctx
->process_hash_table
,
211 (gpointer
) (unsigned long) tid
, newproc
);
212 if (lookup_tid_list(tid
)) {
213 add_filter_tid_list(newproc
);
218 newproc
->comm
= strdup(comm
);
220 host
= lookup_hostname_list(hostname
);
222 host
= add_hostname_list(hostname
, 0);
223 if (!newproc
->host
|| (newproc
->host
!= host
))
224 newproc
->host
= host
;
225 if (is_hostname_filtered(hostname
)) {
226 add_filter_tid_list(newproc
);
233 struct processtop
* update_proc(struct processtop
* proc
, int pid
, int tid
,
234 int ppid
, int vpid
, int vtid
, int vppid
, char *comm
, char *hostname
)
245 if (strcmp(proc
->comm
, comm
) != 0) {
247 proc
->comm
= strdup(comm
);
249 if (hostname
&& !proc
->host
) {
250 host
= lookup_hostname_list(hostname
);
252 host
= add_hostname_list(hostname
, 0);
253 if (!proc
->host
|| (proc
->host
!= host
))
255 if (is_hostname_filtered(hostname
)) {
256 add_filter_tid_list(proc
);
264 * This function just sets the time of death of a process.
265 * When we rotate the cputime we remove it from the process list.
267 void death_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
268 unsigned long timestamp
)
270 struct processtop
*tmp
;
271 tmp
= find_process_tid(ctx
, tid
, comm
);
273 g_hash_table_remove(ctx
->process_hash_table
,
274 (gpointer
) (unsigned long) tid
);
275 if (tmp
&& strcmp(tmp
->comm
, comm
) == 0) {
276 tmp
->death
= timestamp
;
277 ctx
->nbdeadthreads
++;
282 struct processtop
* get_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
283 unsigned long timestamp
, char *hostname
)
285 struct processtop
*tmp
;
287 tmp
= find_process_tid(ctx
, tid
, comm
);
288 if (tmp
&& strcmp(tmp
->comm
, comm
) == 0) {
291 return add_proc(ctx
, tid
, comm
, timestamp
, hostname
);
294 struct processtop
*get_proc_pid(struct lttngtop
*ctx
, int tid
, int pid
,
295 unsigned long timestamp
, char *hostname
)
297 struct processtop
*tmp
;
298 tmp
= find_process_tid(ctx
, tid
, NULL
);
299 if (tmp
&& tmp
->pid
== pid
)
301 return add_proc(ctx
, tid
, NULL
, timestamp
, hostname
);
304 void add_thread(struct processtop
*parent
, struct processtop
*thread
)
307 struct processtop
*tmp
;
312 for (i
= 0; i
< parent
->threads
->len
; i
++) {
313 tmp
= g_ptr_array_index(parent
->threads
, i
);
317 g_ptr_array_add(parent
->threads
, thread
);
320 struct cputime
* add_cpu(int cpu
)
322 struct cputime
*newcpu
;
324 newcpu
= g_new0(struct cputime
, 1);
326 newcpu
->current_task
= NULL
;
327 newcpu
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
329 g_ptr_array_add(lttngtop
.cpu_table
, newcpu
);
333 struct cputime
* get_cpu(int cpu
)
338 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
339 tmp
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
348 * At the end of a sampling period, we need to display the cpu time for each
349 * process and to reset it to zero for the next period
351 void rotate_cputime(unsigned long end
)
355 unsigned long elapsed
;
357 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
358 tmp
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
359 elapsed
= end
- tmp
->task_start
;
360 if (tmp
->current_task
) {
361 tmp
->current_task
->totalcpunsec
+= elapsed
;
362 tmp
->current_task
->threadstotalcpunsec
+= elapsed
;
363 if (tmp
->current_task
->pid
!= tmp
->current_task
->tid
&&
364 tmp
->current_task
->threadparent
) {
365 tmp
->current_task
->threadparent
->threadstotalcpunsec
+= elapsed
;
368 tmp
->task_start
= end
;
372 void reset_perf_counter(gpointer key
, gpointer value
, gpointer user_data
)
374 ((struct perfcounter
*) value
)->count
= 0;
377 void copy_perf_counter(gpointer key
, gpointer value
, gpointer new_table
)
379 struct perfcounter
*newperf
;
381 newperf
= g_new0(struct perfcounter
, 1);
382 newperf
->count
= ((struct perfcounter
*) value
)->count
;
383 newperf
->visible
= ((struct perfcounter
*) value
)->visible
;
384 newperf
->sort
= ((struct perfcounter
*) value
)->sort
;
385 g_hash_table_insert((GHashTable
*) new_table
, strdup(key
), newperf
);
388 void copy_process_table(gpointer key
, gpointer value
, gpointer new_table
)
390 g_hash_table_insert((GHashTable
*) new_table
, key
, value
);
393 void rotate_perfcounter() {
395 struct processtop
*tmp
;
397 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
398 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
399 g_hash_table_foreach(tmp
->perf
, reset_perf_counter
, NULL
);
403 void cleanup_processtop()
406 struct processtop
*tmp
;
407 struct files
*tmpf
; /* a temporary file */
409 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
410 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
411 tmp
->totalcpunsec
= 0;
412 tmp
->threadstotalcpunsec
= 0;
416 for (j
= 0; j
< tmp
->process_files_table
->len
; j
++) {
417 tmpf
= g_ptr_array_index(tmp
->process_files_table
, j
);
422 if (tmpf
->flag
== __NR_close
)
424 tmp
->process_files_table
, j
431 void reset_global_counters()
433 lttngtop
.nbnewproc
= 0;
434 lttngtop
.nbdeadproc
= 0;
435 lttngtop
.nbnewthreads
= 0;
436 lttngtop
.nbdeadthreads
= 0;
437 lttngtop
.nbnewfiles
= 0;
438 lttngtop
.nbclosedfiles
= 0;
441 void copy_global_counters(struct lttngtop
*dst
)
443 dst
->nbproc
= lttngtop
.nbproc
;
444 dst
->nbnewproc
= lttngtop
.nbnewproc
;
445 dst
->nbdeadproc
= lttngtop
.nbdeadproc
;
446 dst
->nbthreads
= lttngtop
.nbthreads
;
447 dst
->nbnewthreads
= lttngtop
.nbnewthreads
;
448 dst
->nbdeadthreads
= lttngtop
.nbdeadthreads
;
449 dst
->nbfiles
= lttngtop
.nbfiles
;
450 dst
->nbnewfiles
= lttngtop
.nbnewfiles
;
451 dst
->nbclosedfiles
= lttngtop
.nbclosedfiles
;
452 reset_global_counters();
455 struct lttngtop
* get_copy_lttngtop(unsigned long start
, unsigned long end
)
459 struct lttngtop
*dst
;
460 struct processtop
*tmp
, *tmp2
, *new;
461 struct cputime
*tmpcpu
, *newcpu
;
462 struct files
*tmpfile
, *newfile
;
463 struct kprobes
*tmpprobe
, *newprobe
;
465 dst
= g_new0(struct lttngtop
, 1);
468 copy_global_counters(dst
);
469 dst
->process_table
= g_ptr_array_new();
470 dst
->files_table
= g_ptr_array_new();
471 dst
->cpu_table
= g_ptr_array_new();
472 dst
->kprobes_table
= g_ptr_array_new();
473 dst
->process_hash_table
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
474 g_hash_table_foreach(lttngtop
.process_hash_table
, copy_process_table
,
475 dst
->process_hash_table
);
479 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
480 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
481 new = g_new0(struct processtop
, 1);
483 memcpy(new, tmp
, sizeof(struct processtop
));
484 new->threads
= g_ptr_array_new();
485 new->comm
= strdup(tmp
->comm
);
486 new->process_files_table
= g_ptr_array_new();
487 new->files_history
= tmp
->files_history
;
488 new->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
489 g_hash_table_foreach(tmp
->perf
, copy_perf_counter
, new->perf
);
491 /* compute the stream speed */
492 if (end
- start
!= 0) {
493 time
= (end
- start
) / NSEC_PER_SEC
;
494 new->fileread
= new->fileread
/(time
);
495 new->filewrite
= new->filewrite
/(time
);
498 for (j
= 0; j
< tmp
->process_files_table
->len
; j
++) {
499 tmpfile
= g_ptr_array_index(tmp
->process_files_table
, j
);
501 newfile
= malloc(sizeof(struct files
));
503 if (tmpfile
!= NULL
) {
504 memcpy(newfile
, tmpfile
, sizeof(struct files
));
506 newfile
->name
= strdup(tmpfile
->name
);
508 newfile
->name
= NULL
;
510 g_ptr_array_add(new->process_files_table
,
512 g_ptr_array_add(dst
->files_table
, newfile
);
514 g_ptr_array_add(new->process_files_table
, NULL
);
515 g_ptr_array_add(dst
->files_table
, NULL
);
518 * if the process died during the last period, we remove all
519 * files associated with if after the copy
521 if (tmp
->death
> 0 && tmp
->death
< end
) {
522 /* FIXME : close the files before */
523 g_ptr_array_remove(tmp
->process_files_table
, tmpfile
);
527 g_ptr_array_add(dst
->process_table
, new);
530 * if the process died during the last period, we remove it from
531 * the current process list after the copy
533 if (tmp
->death
> 0 && tmp
->death
< end
) {
534 g_ptr_array_remove(lttngtop
.process_table
, tmp
);
535 /* FIXME : TRUE does not mean clears the object in it */
536 g_ptr_array_free(tmp
->threads
, TRUE
);
538 g_ptr_array_free(tmp
->process_files_table
, TRUE
);
539 /* FIXME : clear elements */
540 g_hash_table_destroy(tmp
->perf
);
544 rotate_perfcounter();
546 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
547 tmpcpu
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
548 newcpu
= g_new0(struct cputime
, 1);
549 memcpy(newcpu
, tmpcpu
, sizeof(struct cputime
));
550 newcpu
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
551 g_hash_table_foreach(tmpcpu
->perf
, copy_perf_counter
, newcpu
->perf
);
553 * note : we don't care about the current process pointer in the copy
554 * so the reference is invalid after the memcpy
556 g_ptr_array_add(dst
->cpu_table
, newcpu
);
558 if (lttngtop
.kprobes_table
) {
559 for (i
= 0; i
< lttngtop
.kprobes_table
->len
; i
++) {
560 tmpprobe
= g_ptr_array_index(lttngtop
.kprobes_table
, i
);
561 newprobe
= g_new0(struct kprobes
, 1);
562 memcpy(newprobe
, tmpprobe
, sizeof(struct kprobes
));
564 g_ptr_array_add(dst
->kprobes_table
, newprobe
);
567 /* FIXME : better algo */
568 /* create the threads index if required */
569 for (i
= 0; i
< dst
->process_table
->len
; i
++) {
570 tmp
= g_ptr_array_index(dst
->process_table
, i
);
571 if (tmp
->pid
== tmp
->tid
) {
572 for (j
= 0; j
< dst
->process_table
->len
; j
++) {
573 tmp2
= g_ptr_array_index(dst
->process_table
, j
);
574 if (tmp2
->pid
== tmp
->pid
) {
575 tmp2
->threadparent
= tmp
;
576 g_ptr_array_add(tmp
->threads
, tmp2
);
582 // update_global_stats(dst);
583 cleanup_processtop();
589 enum bt_cb_ret
handle_statedump_process_state(struct bt_ctf_event
*call_data
,
592 const struct bt_definition
*scope
;
593 struct processtop
*proc
;
594 unsigned long timestamp
;
595 int64_t pid
, tid
, ppid
, vtid
, vpid
, vppid
;
596 char *procname
= NULL
, *hostname
= NULL
;
598 timestamp
= bt_ctf_get_timestamp(call_data
);
599 if (timestamp
== -1ULL)
602 scope
= bt_ctf_get_top_level_scope(call_data
,
604 pid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
606 if (bt_ctf_field_get_error()) {
607 fprintf(stderr
, "Missing pid context info\n");
610 ppid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
612 if (bt_ctf_field_get_error()) {
615 tid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
617 if (bt_ctf_field_get_error()) {
618 fprintf(stderr
, "Missing tid context info\n");
621 vtid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
623 if (bt_ctf_field_get_error()) {
624 fprintf(stderr
, "Missing vtid context info\n");
627 vpid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
629 if (bt_ctf_field_get_error()) {
630 fprintf(stderr
, "Missing vpid context info\n");
633 vppid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
635 if (bt_ctf_field_get_error()) {
636 fprintf(stderr
, "Missing vppid context info\n");
640 scope
= bt_ctf_get_top_level_scope(call_data
,
642 procname
= bt_ctf_get_char_array(bt_ctf_get_field(call_data
,
644 if (bt_ctf_field_get_error()) {
645 fprintf(stderr
, "Missing process name context info\n");
649 proc
= find_process_tid(<tngtop
, tid
, procname
);
651 proc
= add_proc(<tngtop
, tid
, procname
, timestamp
, hostname
);
652 update_proc(proc
, pid
, tid
, ppid
, vpid
, vtid
, vppid
, procname
, hostname
);
656 proc
->comm
= strdup(procname
);
664 return BT_CB_ERROR_STOP
;
667 struct tm
format_timestamp(uint64_t timestamp
)
670 uint64_t ts_sec
= 0, ts_nsec
;
674 ts_sec
+= ts_nsec
/ NSEC_PER_SEC
;
675 ts_nsec
= ts_nsec
% NSEC_PER_SEC
;
677 time_s
= (time_t) ts_sec
;
679 localtime_r(&time_s
, &tm
);
684 int *lookup_tid_list(int tid
)
686 if (!tid_filter_list
)
689 return g_hash_table_lookup(tid_filter_list
, (gpointer
) &tid
);
692 struct host
*lookup_hostname_list(const char *hostname
)
694 if (!hostname
|| !global_host_list
)
697 return g_hash_table_lookup(global_host_list
, (gpointer
) hostname
);
700 int is_hostname_filtered(const char *hostname
)
704 host
= lookup_hostname_list(hostname
);
710 int *lookup_filter_tid_list(int tid
)
712 return g_hash_table_lookup(global_filter_list
, (gpointer
) &tid
);
715 void add_filter_tid_list(struct processtop
*proc
)
717 unsigned long *hash_tid
;
719 hash_tid
= malloc(sizeof(unsigned long));
720 *hash_tid
= proc
->tid
;
721 g_hash_table_insert(global_filter_list
,
722 (gpointer
) (unsigned long) hash_tid
, proc
);
725 void remove_filter_tid_list(int tid
)
727 g_hash_table_remove(global_filter_list
,
728 (gpointer
) (unsigned long) &tid
);
731 struct host
*add_hostname_list(char *hostname
, int filter
)
735 host
= lookup_hostname_list(hostname
);
739 host
= g_new0(struct host
, 1);
740 host
->hostname
= strdup(hostname
);
741 host
->filter
= filter
;
742 g_hash_table_insert(global_host_list
,
743 (gpointer
) host
->hostname
,
749 void update_hostname_filter(struct host
*host
)
751 struct processtop
*tmp
;
754 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
755 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
756 if (tmp
->host
== host
) {
758 add_filter_tid_list(tmp
);
760 remove_filter_tid_list(tmp
->tid
);
765 char *lookup_procname(const char *procname
)
767 if (!procname
|| !global_procname_list
)
770 return g_hash_table_lookup(global_procname_list
, (gpointer
) procname
);
773 char *add_procname_list(char *procname
, int filter
)
777 proc
= lookup_procname(procname
);
781 proc
= strdup(procname
);
782 g_hash_table_insert(global_procname_list
,
783 (gpointer
) procname
, (gpointer
) procname
);
This page took 0.051631 seconds and 4 git commands to generate.