500c80d674f9056ced114e75a54af1e04fc51fc2
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 fprintf(stderr
, "Missing tid context info\n");
55 uint64_t get_context_pid(const struct bt_ctf_event
*event
)
57 const struct bt_definition
*scope
;
60 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
61 pid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
63 if (bt_ctf_field_get_error()) {
64 fprintf(stderr
, "Missing pid context info\n");
71 uint64_t get_context_ppid(const struct bt_ctf_event
*event
)
73 const struct bt_definition
*scope
;
76 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
77 ppid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
79 if (bt_ctf_field_get_error()) {
80 fprintf(stderr
, "Missing ppid context info\n");
87 uint64_t get_context_vtid(const struct bt_ctf_event
*event
)
89 const struct definition
*scope
;
92 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
93 vtid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
95 if (bt_ctf_field_get_error()) {
102 uint64_t get_context_vpid(const struct bt_ctf_event
*event
)
104 const struct definition
*scope
;
107 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
108 vpid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
110 if (bt_ctf_field_get_error()) {
117 uint64_t get_context_vppid(const struct bt_ctf_event
*event
)
119 const struct definition
*scope
;
122 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
123 vppid
= bt_ctf_get_int64(bt_ctf_get_field(event
,
125 if (bt_ctf_field_get_error()) {
132 char *get_context_comm(const struct bt_ctf_event
*event
)
134 const struct bt_definition
*scope
;
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");
148 char *get_context_hostname(const struct bt_ctf_event
*event
)
150 const struct definition
*scope
;
153 scope
= bt_ctf_get_top_level_scope(event
, BT_STREAM_EVENT_CONTEXT
);
154 hostname
= bt_ctf_get_char_array(bt_ctf_get_field(event
,
155 scope
, "_hostname"));
156 if (bt_ctf_field_get_error()) {
164 * To get the parent process, put the pid in the tid field
165 * because the parent process gets pid = tid
167 struct processtop
*find_process_tid(struct lttngtop
*ctx
, int tid
, char *comm
)
169 struct processtop
*tmp
;
171 tmp
= g_hash_table_lookup(ctx
->process_hash_table
,
172 (gconstpointer
) (unsigned long) tid
);
177 struct processtop
* add_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
178 unsigned long timestamp
)
180 struct processtop
*newproc
;
182 /* if the PID already exists, we just rename the process */
183 /* FIXME : need to integrate with clone/fork/exit to be accurate */
184 newproc
= find_process_tid(ctx
, tid
, comm
);
187 newproc
= g_new0(struct processtop
, 1);
189 newproc
->birth
= timestamp
;
190 newproc
->process_files_table
= g_ptr_array_new();
191 newproc
->files_history
= NULL
;
192 newproc
->totalfileread
= 0;
193 newproc
->totalfilewrite
= 0;
194 newproc
->fileread
= 0;
195 newproc
->filewrite
= 0;
196 newproc
->syscall_info
= NULL
;
197 newproc
->threadparent
= NULL
;
198 newproc
->threads
= g_ptr_array_new();
199 newproc
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
200 g_ptr_array_add(ctx
->process_table
, newproc
);
201 g_hash_table_insert(ctx
->process_hash_table
,
202 (gpointer
) (unsigned long) tid
, newproc
);
203 if (lookup_tid_list(tid
)) {
204 add_filter_tid_list(tid
, newproc
);
209 newproc
->comm
= strdup(comm
);
214 struct processtop
* update_proc(struct processtop
* proc
, int pid
, int tid
,
215 int ppid
, int vpid
, int vtid
, int vppid
, char *comm
, char *hostname
)
224 if (strcmp(proc
->comm
, comm
) != 0) {
226 proc
->comm
= strdup(comm
);
229 if (proc
->hostname
&& strcmp(proc
->hostname
, hostname
) != 0) {
230 free(proc
->hostname
);
232 proc
->hostname
= strdup(hostname
);
233 if (lookup_hostname_list(hostname
)) {
234 add_filter_tid_list(tid
, proc
);
242 * This function just sets the time of death of a process.
243 * When we rotate the cputime we remove it from the process list.
245 void death_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
246 unsigned long timestamp
)
248 struct processtop
*tmp
;
249 tmp
= find_process_tid(ctx
, tid
, comm
);
251 g_hash_table_remove(ctx
->process_hash_table
,
252 (gpointer
) (unsigned long) tid
);
253 if (tmp
&& strcmp(tmp
->comm
, comm
) == 0) {
254 tmp
->death
= timestamp
;
255 ctx
->nbdeadthreads
++;
260 struct processtop
* get_proc(struct lttngtop
*ctx
, int tid
, char *comm
,
261 unsigned long timestamp
)
263 struct processtop
*tmp
;
264 tmp
= find_process_tid(ctx
, tid
, comm
);
265 if (tmp
&& strcmp(tmp
->comm
, comm
) == 0)
267 return add_proc(ctx
, tid
, comm
, timestamp
);
270 struct processtop
*get_proc_pid(struct lttngtop
*ctx
, int tid
, int pid
,
271 unsigned long timestamp
)
273 struct processtop
*tmp
;
274 tmp
= find_process_tid(ctx
, tid
, NULL
);
275 if (tmp
&& tmp
->pid
== pid
)
277 return add_proc(ctx
, tid
, "Unknown", timestamp
);
280 void add_thread(struct processtop
*parent
, struct processtop
*thread
)
283 struct processtop
*tmp
;
288 for (i
= 0; i
< parent
->threads
->len
; i
++) {
289 tmp
= g_ptr_array_index(parent
->threads
, i
);
293 g_ptr_array_add(parent
->threads
, thread
);
296 struct cputime
* add_cpu(int cpu
)
298 struct cputime
*newcpu
;
300 newcpu
= g_new0(struct cputime
, 1);
302 newcpu
->current_task
= NULL
;
303 newcpu
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
305 g_ptr_array_add(lttngtop
.cpu_table
, newcpu
);
309 struct cputime
* get_cpu(int cpu
)
314 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
315 tmp
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
324 * At the end of a sampling period, we need to display the cpu time for each
325 * process and to reset it to zero for the next period
327 void rotate_cputime(unsigned long end
)
331 unsigned long elapsed
;
333 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
334 tmp
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
335 elapsed
= end
- tmp
->task_start
;
336 if (tmp
->current_task
) {
337 tmp
->current_task
->totalcpunsec
+= elapsed
;
338 tmp
->current_task
->threadstotalcpunsec
+= elapsed
;
339 if (tmp
->current_task
->pid
!= tmp
->current_task
->tid
&&
340 tmp
->current_task
->threadparent
) {
341 tmp
->current_task
->threadparent
->threadstotalcpunsec
+= elapsed
;
344 tmp
->task_start
= end
;
348 void reset_perf_counter(gpointer key
, gpointer value
, gpointer user_data
)
350 ((struct perfcounter
*) value
)->count
= 0;
353 void copy_perf_counter(gpointer key
, gpointer value
, gpointer new_table
)
355 struct perfcounter
*newperf
;
357 newperf
= g_new0(struct perfcounter
, 1);
358 newperf
->count
= ((struct perfcounter
*) value
)->count
;
359 newperf
->visible
= ((struct perfcounter
*) value
)->visible
;
360 newperf
->sort
= ((struct perfcounter
*) value
)->sort
;
361 g_hash_table_insert((GHashTable
*) new_table
, strdup(key
), newperf
);
364 void copy_process_table(gpointer key
, gpointer value
, gpointer new_table
)
366 g_hash_table_insert((GHashTable
*) new_table
, key
, value
);
369 void rotate_perfcounter() {
371 struct processtop
*tmp
;
372 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
373 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
374 g_hash_table_foreach(tmp
->perf
, reset_perf_counter
, NULL
);
378 void cleanup_processtop()
381 struct processtop
*tmp
;
382 struct files
*tmpf
; /* a temporary file */
384 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
385 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
386 tmp
->totalcpunsec
= 0;
387 tmp
->threadstotalcpunsec
= 0;
391 for (j
= 0; j
< tmp
->process_files_table
->len
; j
++) {
392 tmpf
= g_ptr_array_index(tmp
->process_files_table
, j
);
397 if (tmpf
->flag
== __NR_close
)
399 tmp
->process_files_table
, j
406 void reset_global_counters()
408 lttngtop
.nbnewproc
= 0;
409 lttngtop
.nbdeadproc
= 0;
410 lttngtop
.nbnewthreads
= 0;
411 lttngtop
.nbdeadthreads
= 0;
412 lttngtop
.nbnewfiles
= 0;
413 lttngtop
.nbclosedfiles
= 0;
416 void copy_global_counters(struct lttngtop
*dst
)
418 dst
->nbproc
= lttngtop
.nbproc
;
419 dst
->nbnewproc
= lttngtop
.nbnewproc
;
420 dst
->nbdeadproc
= lttngtop
.nbdeadproc
;
421 dst
->nbthreads
= lttngtop
.nbthreads
;
422 dst
->nbnewthreads
= lttngtop
.nbnewthreads
;
423 dst
->nbdeadthreads
= lttngtop
.nbdeadthreads
;
424 dst
->nbfiles
= lttngtop
.nbfiles
;
425 dst
->nbnewfiles
= lttngtop
.nbnewfiles
;
426 dst
->nbclosedfiles
= lttngtop
.nbclosedfiles
;
427 reset_global_counters();
430 struct lttngtop
* get_copy_lttngtop(unsigned long start
, unsigned long end
)
434 struct lttngtop
*dst
;
435 struct processtop
*tmp
, *tmp2
, *new;
436 struct cputime
*tmpcpu
, *newcpu
;
437 struct files
*tmpfile
, *newfile
;
438 struct kprobes
*tmpprobe
, *newprobe
;
440 dst
= g_new0(struct lttngtop
, 1);
443 copy_global_counters(dst
);
444 dst
->process_table
= g_ptr_array_new();
445 dst
->files_table
= g_ptr_array_new();
446 dst
->cpu_table
= g_ptr_array_new();
447 dst
->kprobes_table
= g_ptr_array_new();
448 dst
->process_hash_table
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
449 g_hash_table_foreach(lttngtop
.process_hash_table
, copy_process_table
,
450 dst
->process_hash_table
);
454 for (i
= 0; i
< lttngtop
.process_table
->len
; i
++) {
455 tmp
= g_ptr_array_index(lttngtop
.process_table
, i
);
456 new = g_new0(struct processtop
, 1);
458 memcpy(new, tmp
, sizeof(struct processtop
));
459 new->threads
= g_ptr_array_new();
460 new->comm
= strdup(tmp
->comm
);
461 new->process_files_table
= g_ptr_array_new();
462 new->files_history
= tmp
->files_history
;
463 new->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
464 g_hash_table_foreach(tmp
->perf
, copy_perf_counter
, new->perf
);
466 /* compute the stream speed */
467 if (end
- start
!= 0) {
468 time
= (end
- start
) / NSEC_PER_SEC
;
469 new->fileread
= new->fileread
/(time
);
470 new->filewrite
= new->filewrite
/(time
);
473 for (j
= 0; j
< tmp
->process_files_table
->len
; j
++) {
474 tmpfile
= g_ptr_array_index(tmp
->process_files_table
, j
);
476 newfile
= malloc(sizeof(struct files
));
478 if (tmpfile
!= NULL
) {
479 memcpy(newfile
, tmpfile
, sizeof(struct files
));
480 newfile
->name
= strdup(tmpfile
->name
);
482 g_ptr_array_add(new->process_files_table
,
484 g_ptr_array_add(dst
->files_table
, newfile
);
486 g_ptr_array_add(new->process_files_table
, NULL
);
487 g_ptr_array_add(dst
->files_table
, NULL
);
490 * if the process died during the last period, we remove all
491 * files associated with if after the copy
493 if (tmp
->death
> 0 && tmp
->death
< end
) {
494 /* FIXME : close the files before */
495 g_ptr_array_remove(tmp
->process_files_table
, tmpfile
);
499 g_ptr_array_add(dst
->process_table
, new);
502 * if the process died during the last period, we remove it from
503 * the current process list after the copy
505 if (tmp
->death
> 0 && tmp
->death
< end
) {
506 g_ptr_array_remove(lttngtop
.process_table
, tmp
);
507 /* FIXME : TRUE does not mean clears the object in it */
508 g_ptr_array_free(tmp
->threads
, TRUE
);
510 g_ptr_array_free(tmp
->process_files_table
, TRUE
);
511 /* FIXME : clear elements */
512 g_hash_table_destroy(tmp
->perf
);
516 rotate_perfcounter();
518 for (i
= 0; i
< lttngtop
.cpu_table
->len
; i
++) {
519 tmpcpu
= g_ptr_array_index(lttngtop
.cpu_table
, i
);
520 newcpu
= g_new0(struct cputime
, 1);
521 memcpy(newcpu
, tmpcpu
, sizeof(struct cputime
));
522 newcpu
->perf
= g_hash_table_new(g_str_hash
, g_str_equal
);
523 g_hash_table_foreach(tmpcpu
->perf
, copy_perf_counter
, newcpu
->perf
);
525 * note : we don't care about the current process pointer in the copy
526 * so the reference is invalid after the memcpy
528 g_ptr_array_add(dst
->cpu_table
, newcpu
);
530 if (lttngtop
.kprobes_table
) {
531 for (i
= 0; i
< lttngtop
.kprobes_table
->len
; i
++) {
532 tmpprobe
= g_ptr_array_index(lttngtop
.kprobes_table
, i
);
533 newprobe
= g_new0(struct kprobes
, 1);
534 memcpy(newprobe
, tmpprobe
, sizeof(struct kprobes
));
536 g_ptr_array_add(dst
->kprobes_table
, newprobe
);
539 /* FIXME : better algo */
540 /* create the threads index if required */
541 for (i
= 0; i
< dst
->process_table
->len
; i
++) {
542 tmp
= g_ptr_array_index(dst
->process_table
, i
);
543 if (tmp
->pid
== tmp
->tid
) {
544 for (j
= 0; j
< dst
->process_table
->len
; j
++) {
545 tmp2
= g_ptr_array_index(dst
->process_table
, j
);
546 if (tmp2
->pid
== tmp
->pid
) {
547 tmp2
->threadparent
= tmp
;
548 g_ptr_array_add(tmp
->threads
, tmp2
);
554 // update_global_stats(dst);
555 cleanup_processtop();
561 enum bt_cb_ret
handle_statedump_process_state(struct bt_ctf_event
*call_data
,
564 const struct bt_definition
*scope
;
565 struct processtop
*proc
;
566 unsigned long timestamp
;
567 int64_t pid
, tid
, ppid
, vtid
, vpid
, vppid
;
570 timestamp
= bt_ctf_get_timestamp(call_data
);
571 if (timestamp
== -1ULL)
574 scope
= bt_ctf_get_top_level_scope(call_data
,
576 pid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
578 if (bt_ctf_field_get_error()) {
579 fprintf(stderr
, "Missing pid context info\n");
582 ppid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
584 if (bt_ctf_field_get_error()) {
585 fprintf(stderr
, "Missing ppid context info\n");
588 tid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
590 if (bt_ctf_field_get_error()) {
591 fprintf(stderr
, "Missing tid context info\n");
594 vtid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
596 if (bt_ctf_field_get_error()) {
597 fprintf(stderr
, "Missing vtid context info\n");
600 vpid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
602 if (bt_ctf_field_get_error()) {
603 fprintf(stderr
, "Missing vpid context info\n");
606 vppid
= bt_ctf_get_int64(bt_ctf_get_field(call_data
,
608 if (bt_ctf_field_get_error()) {
609 fprintf(stderr
, "Missing vppid context info\n");
613 scope
= bt_ctf_get_top_level_scope(call_data
,
615 procname
= bt_ctf_get_char_array(bt_ctf_get_field(call_data
,
617 if (bt_ctf_field_get_error()) {
618 fprintf(stderr
, "Missing process name context info\n");
622 proc
= find_process_tid(<tngtop
, tid
, procname
);
624 proc
= add_proc(<tngtop
, tid
, procname
, timestamp
);
625 update_proc(proc
, pid
, tid
, ppid
, vpid
, vtid
, vppid
, procname
, NULL
);
629 proc
->comm
= strdup(procname
);
636 return BT_CB_ERROR_STOP
;
639 struct tm
format_timestamp(uint64_t timestamp
)
642 uint64_t ts_sec
= 0, ts_nsec
;
646 ts_sec
+= ts_nsec
/ NSEC_PER_SEC
;
647 ts_nsec
= ts_nsec
% NSEC_PER_SEC
;
649 time_s
= (time_t) ts_sec
;
651 localtime_r(&time_s
, &tm
);
656 int *lookup_tid_list(int tid
)
661 return g_hash_table_lookup(tid_list
, (gpointer
) &tid
);
664 char *lookup_hostname_list(const char *hostname
)
666 if (!hostname
|| !hostname_list
)
669 return g_hash_table_lookup(hostname_list
, (gpointer
) hostname
);
672 int *lookup_filter_tid_list(int tid
)
674 return g_hash_table_lookup(global_filter_list
, (gpointer
) &tid
);
677 void add_filter_tid_list(int tid
, struct processtop
*newproc
)
679 unsigned long *hash_tid
;
681 hash_tid
= malloc(sizeof(unsigned long));
683 g_hash_table_insert(global_filter_list
,
684 (gpointer
) (unsigned long) hash_tid
, newproc
);
687 void remove_filter_tid_list(int tid
)
689 g_hash_table_remove(global_filter_list
,
690 (gpointer
) (unsigned long) &tid
);
This page took 0.043749 seconds and 3 git commands to generate.